# -*- coding: utf8 -*-

#***************************************************************************
#*                                                                         *
#*   Copyright (c) 2009 Yorik van Havre <yorik@uncreated.net>              *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   This program is distributed in the hope that it will be useful,       *
#*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#*   GNU Library General Public License for more details.                  *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with this program; if not, write to the Free Software   *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#***************************************************************************

__title__="FreeCAD Draft Workbench - DXF importer/exporter"
__author__ = "Yorik van Havre <yorik@uncreated.net>"
__url__ = ["http://www.freecadweb.org"]

## @package importDXF
#  \ingroup DRAFT
#  \brief DXF file importer & exporter
#
# This module provides support for importing and exporting Autodesk DXF files

'''
This script uses a DXF-parsing library created by Stani,
Kitsu and Migius for Blender

imports:
line, polylines, lwpolylines, arcs, circles, texts,
mtexts, layers (as groups), colors

exports:
lines, polylines, lwpolylines, circles, arcs,
texts, colors,layers (from groups)
'''

TEXTSCALING = 1.35 # scaling factor between autocad font sizes and coin font sizes
CURRENTDXFLIB = 1.40 # the minimal version of the dxfLibrary needed to run

import sys, FreeCAD, os, Part, math, re, string, Mesh, Draft, DraftVecUtils, DraftGeomUtils
from Draft import _Dimension, _ViewProviderDimension
from FreeCAD import Vector

gui = FreeCAD.GuiUp
draftui = None
if gui:
    import FreeCADGui
    try:
        draftui = FreeCADGui.draftToolBar
    except (AttributeError,NameError):
        draftui = None
        
dxfReader = None
dxfColorMap = None
dxfLibrary = None

if open.__module__ in ['__builtin__','io']:
    pythonopen = open # to distinguish python built-in open function from the one declared here


def errorDXFLib(gui):
    p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
    dxfAllowDownload = p.GetBool("dxfAllowDownload",False)
    if dxfAllowDownload:
        files = ['dxfColorMap.py','dxfImportObjects.py','dxfLibrary.py','dxfReader.py']
        baseurl = 'https://raw.githubusercontent.com/yorikvanhavre/Draft-dxf-importer/'+'{0:.2f}'.format(CURRENTDXFLIB)+"/"
        import ArchCommands
        from FreeCAD import Base
        progressbar = Base.ProgressIndicator()
        progressbar.start("Downloading files...",4)
        for f in files:
            progressbar.next()
            p = None
            p = ArchCommands.download(baseurl+f,force=True)
            if not p:
                if gui:
                    from PySide import QtGui, QtCore
                    from DraftTools import translate
                    message = translate("Draft","""Download of dxf libraries failed.
Please install the dxf Library addon manually
from menu Tools -> Addon Manager""")
                    QtGui.QMessageBox.information(None,"",message)
                else:
                    FreeCAD.Console.PrintWarning("The DXF import/export libraries needed by FreeCAD to handle the DXF format are not installed.\n")
                    FreeCAD.Console.PrintWarning("Please install the dxf Library addon from Tools -> Addons Manager\n")
                break
        progressbar.stop()
        sys.path.append(FreeCAD.ConfigGet("UserAppData"))
    else:
        if gui:
            from PySide import QtGui, QtCore
            from DraftTools import translate
            message = translate('draft',"""The DXF import/export libraries needed by FreeCAD to handle
the DXF format were not found on this system.
Please either enable FreeCAD to download these libraries:
  1 - Load Draft workbench
  2 - Menu Edit > Preferences > Import-Export > DXF > Enable downloads
Or download these libraries manually, as explained on
https://github.com/yorikvanhavre/Draft-dxf-importer
To enabled FreeCAD to download these libraries, answer Yes.""")
            if sys.version_info.major < 3:
                if not isinstance(message,unicode):
                    message = message.decode('utf8')
            reply = QtGui.QMessageBox.question(None,"",message,
                QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No)
            if reply == QtGui.QMessageBox.Yes:
                p.SetBool("dxfAllowDownload",True)
                errorDXFLib(gui)
            if reply == QtGui.QMessageBox.No:
                pass
        else:
            FreeCAD.Console.PrintWarning("The DXF import/export libraries needed by FreeCAD to handle the DXF format are not installed.\n")
            if float(FreeCAD.Version()[0]+"."+FreeCAD.Version()[1]) >= 0.17:
                FreeCAD.Console.PrintWarning("Please install the dxf Library addon from Tools -> Addons Manager\n")
            else:
                FreeCAD.Console.PrintWarning("Please check https://github.com/yorikvanhavre/Draft-dxf-importer\n")


def getDXFlibs():
    "loads the DXF python libraries"
    try:
        if FreeCAD.ConfigGet("UserAppData") not in sys.path:
            sys.path.append(FreeCAD.ConfigGet("UserAppData"))
        global dxfLibrary,dxfColorMap,dxfReader
        import dxfLibrary
        import dxfColorMap
        import dxfReader
    except ImportError:
        libsok = False
        FreeCAD.Console.PrintWarning("DXF libraries not found. Trying to download...\n")
    else:
        if "v"+str(CURRENTDXFLIB) in dxfLibrary.__version__:
            libsok = True
        else:
            FreeCAD.Console.PrintWarning("DXF libraries need to be updated. Trying to download...\n")
            libsok = False
    if not libsok:
        errorDXFLib(gui)
        try:
            import dxfColorMap, dxfLibrary, dxfReader
        except ImportError:
            dxfReader = None
            dxfLibrary = None
            FreeCAD.Console.PrintWarning("DXF libraries not available. Aborting.\n")

def prec():
    "returns the current Draft precision level"
    return Draft.getParam("precision",6)

def decodeName(name):
    "decodes encoded strings"
    try:
        decodedName = (name.decode("utf8"))
    except UnicodeDecodeError:
        try:
            decodedName = (name.decode("latin1"))
        except UnicodeDecodeError:
                print("dxf: error: couldn't determine character encoding")
                decodedName = name
    except AttributeError:
        # this is python3 (nothing to do)
        decodedName = name
    return decodedName

def deformat(text):
    "removes weird formats in texts and wipes UTF characters"
    # remove ACAD string formatation
    #t = re.sub('{([^!}]([^}]|\n)*)}', '', text)
    #print("input text: ",text)
    t = text.strip("{}")
    t = re.sub("\\\.*?;","",t)
    # replace UTF codes by utf chars
    sts = re.split("\\\\(U\+....)",t)
    ns = u""
    for ss in sts:
        #print(ss, type(ss))
        if ss.startswith("U+"):
            ucode = "0x"+ss[2:]
            ns += unichr(eval(ucode)) #Python3 - unichr doesn't exist anymore
        else:
            try:
                ns += ss.decode("utf8")
            except UnicodeError:
                try:
                    ns += ss.decode("latin1")
                except UnicodeError:
                    print("unable to decode text: ",text)
            except AttributeError:
                # this is python3 (nothing to do)
                ns += ss
    t = ns
    # replace degrees, diameters chars
    t = re.sub('%%d',u'°',t)
    t = re.sub('%%c',u'Ø',t)
    t = re.sub('%%D',u'°',t)
    t = re.sub('%%C',u'Ø',t)
    #print("output text: ",t)
    return t

def locateLayer(wantedLayer,color=None):
    "returns layer group and creates it if needed"
    wantedLayerName = decodeName(wantedLayer)
    for l in layers:
        if wantedLayerName==l.Label:
            return l
    if dxfUseDraftVisGroups:
        newLayer = Draft.makeVisGroup(name=wantedLayer)
    else:
        newLayer = doc.addObject("App::DocumentObjectGroup",wantedLayer)
    newLayer.Label = wantedLayerName
    layers.append(newLayer)
    return newLayer

def getdimheight(style):
    "returns the dimension text height from the given dimstyle"
    for t in drawing.tables.data:
        if t.name == 'dimstyle':
            for a in t.data:
                if hasattr(a,"type"):
                    if a.type == "dimstyle":
                        if rawValue(a,2) == style:
                            return rawValue(a,140)
    return 1

def calcBulge(v1,bulge,v2):
    '''
    calculates intermediary vertex for curved segments.
    algorithm from http://www.afralisp.net/lisp/Bulges1.htm
    '''
    chord = v2.sub(v1)
    sagitta = (bulge * chord.Length)/2
    perp = chord.cross(Vector(0,0,1))
    startpoint = v1.add(chord.multiply(0.5))
    if not DraftVecUtils.isNull(perp): perp.normalize()
    endpoint = perp.multiply(sagitta)
    return startpoint.add(endpoint)

def getGroup(ob):
    "checks if the object is part of a group"
    for i in FreeCAD.ActiveDocument.Objects:
        if i.isDerivedFrom("App::DocumentObjectGroup"):
            for j in i.Group:
                if (j == ob):
                    return i.Label
    return "0"

def getACI(ob,text=False):
    "gets the ACI color closest to the objects color"
    if not gui:
        return 0
    else:
        if text:
            col=ob.ViewObject.TextColor
        else:
            col=ob.ViewObject.LineColor
        aci=[0,442]
        for i in range (255,-1,-1):
            ref=dxfColorMap.color_map[i]
            dist=((ref[0]-col[0])**2 + (ref[1]-col[1])**2 + (ref[2]-col[2])**2)
            if (dist <= aci[1]): aci=[i,dist]
        return aci[0]

def rawValue(entity,code):
    "returns the  value of a DXF code in an entity section"
    value = None
    for pair in entity.data:
        if pair[0] == code:
            value = pair[1]
    return value

def getMultiplePoints(entity):
    "scans the given entity for multiple points (paths, leaders, etc)"
    pts = []
    for d in entity.data:
        if d[0] == 10:
            pts.append([d[1]])
        elif d[0] in [20,30]:
            pts[-1].append(d[1])
    pts.reverse()
    points = []
    for p in pts:
        if len(p) == 3:
            points.append(Vector(p[0],p[1],p[2]))
        else:
            points.append(Vector(p[0],p[1],0))
    return points

def isBrightBackground():
    "checks if the current viewport background is bright"
    p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
    if p.GetBool("Gradient"):
        c1 = p.GetUnsigned("BackgroundColor2")
        c2 = p.GetUnsigned("BackgroundColor3")
        r1 = float((c1>>24)&0xFF)
        g1 = float((c1>>16)&0xFF)
        b1 = float((c1>>8)&0xFF)
        r2 = float((c2>>24)&0xFF)
        g2 = float((c2>>16)&0xFF)
        b2 = float((c2>>8)&0xFF)
        v1 = FreeCAD.Vector(r1,g1,b1)
        v2 = FreeCAD.Vector(r2,g2,b2)
        v = v2.sub(v1)
        v.multiply(0.5)
        cv = v1.add(v)
    else:
        c1 = p.GetUnsigned("BackgroundColor")
        r1 = float((c1>>24)&0xFF)
        g1 = float((c1>>16)&0xFF)
        b1 = float((c1>>8)&0xFF)
        cv = FreeCAD.Vector(r1,g1,b1)
    value = cv.x*.3 + cv.y*.59 + cv.z*.11
    if value < 128:
        return False
    else:
        return True

def getGroupColor(dxfobj,index=False):
    "get color of bylayer stuff"
    name = dxfobj.layer
    for table in drawing.tables.get_type("table"):
        if table.name == "layer":
            for l in table.get_type("layer"):
                if l.name == name:
                    if index:
                        return l.color
                    else:
                        if (l.color == 7) and dxfBrightBackground:
                            return [0.0,0.0,0.0]
                        else:
                            if isinstance(l.color,int):
                                if l.color > 0:
                                    return dxfColorMap.color_map[l.color]
    return [0.0,0.0,0.0]

def getColor():
    if gui and draftui:
        r = float(draftui.color.red()/255.0)
        g = float(draftui.color.green()/255.0)
        b = float(draftui.color.blue()/255.0)
        return (r,g,b,0.0)
    else:
        p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/View")
        c = p.GetUnsigned("DefaultShapeLineColor",0)
        r = float(((c>>24)&0xFF)/255)
        g = float(((c>>16)&0xFF)/255)
        b = float(((c>>8)&0xFF)/255)
        return (r,g,b,0.0)

def formatObject(obj,dxfobj=None):
    "applies color and linetype to objects"
    if dxfGetColors and dxfobj and hasattr(dxfobj,"color_index"):
        if hasattr(obj.ViewObject,"TextColor"):
            if dxfobj.color_index == 256:
                cm = getGroupColor(dxfobj)[:3]
            else:
                cm = dxfColorMap.color_map[dxfobj.color_index]
            obj.ViewObject.TextColor = (cm[0],cm[1],cm[2])
        elif hasattr(obj.ViewObject,"LineColor"):
            if dxfobj.color_index == 256:
                cm = getGroupColor(dxfobj)
            elif (dxfobj.color_index == 7) and dxfBrightBackground:
                cm = [0.0,0.0,0.0]
            else:
                cm = dxfColorMap.color_map[dxfobj.color_index]
            obj.ViewObject.LineColor = (cm[0],cm[1],cm[2],0.0)
    else:
        if hasattr(obj.ViewObject,"TextColor"):
            obj.ViewObject.TextColor = dxfDefaultColor
        elif hasattr(obj.ViewObject,"LineColor"):
            obj.ViewObject.LineColor = dxfDefaultColor

def vec(pt):
    "returns a rounded and scaled Vector from a dxf point, or rounded and scaled value"
    if isinstance(pt,float) or isinstance(pt,int):
        v = round(pt,prec())
        if dxfScaling != 1:
            v = v * dxfScaling
    else:
        v = FreeCAD.Vector(round(pt[0],prec()),round(pt[1],prec()),round(pt[2],prec()))
        if dxfScaling != 1:
            v.multiply(dxfScaling)
    return v

def drawLine(line,forceShape=False):
    "returns a Part shape from a dxf line"
    if (len(line.points) > 1):
        v1=vec(line.points[0])
        v2=vec(line.points[1])
        if not DraftVecUtils.equals(v1,v2):
            try:
                if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
                    return Draft.makeWire([v1,v2])
                else:
                    return Part.LineSegment(v1,v2).toShape()
            except Part.OCCError:
                warn(line)
    return None

def drawPolyline(polyline,forceShape=False,num=None):
    "returns a Part shape from a dxf polyline"
    if (len(polyline.points) > 1):
        edges = []
        curves = False
        verts = []
        for p in range(len(polyline.points)-1):
            p1 = polyline.points[p]
            p2 = polyline.points[p+1]
            v1 = vec(p1)
            v2 = vec(p2)
            verts.append(v1)
            if not DraftVecUtils.equals(v1,v2):
                if polyline.points[p].bulge:
                    curves = True
                    cv = calcBulge(v1,polyline.points[p].bulge,v2)
                    if DraftVecUtils.isColinear([v1,cv,v2]):
                        try: edges.append(Part.LineSegment(v1,v2).toShape())
                        except Part.OCCError: warn(polyline,num)
                    else:
                        try: edges.append(Part.Arc(v1,cv,v2).toShape())
                        except Part.OCCError: warn(polyline,num)
                else:
                    try: edges.append(Part.LineSegment(v1,v2).toShape())
                    except Part.OCCError: warn(polyline,num)
        verts.append(v2)
        if polyline.closed:
            p1 = polyline.points[len(polyline.points)-1]
            p2 = polyline.points[0]
            v1 = vec(p1)
            v2 = vec(p2)
            cv = calcBulge(v1,polyline.points[-1].bulge,v2)
            if not DraftVecUtils.equals(v1,v2):
                if DraftVecUtils.isColinear([v1,cv,v2]):
                    try:
                        edges.append(Part.LineSegment(v1,v2).toShape())
                    except Part.OCCError:
                        warn(polyline,num)
                else:
                    try:
                        edges.append(Part.Arc(v1,cv,v2).toShape())
                    except Part.OCCError:
                        warn(polyline,num)
        if edges:
            try:
                width = rawValue(polyline,43)
                if width and dxfRenderPolylineWidth:
                    w = Part.Wire(edges)
                    w1 = w.makeOffset(width/2)
                    if polyline.closed:
                        w2 = w.makeOffset(-width/2)
                        w1 = Part.Face(w1)
                        w2 = Part.Face(w2)
                        if w1.BoundBox.DiagonalLength > w2.BoundBox.DiagonalLength:
                            return w1.cut(w2)
                        else:
                            return w2.cut(w1)
                    else:
                        return Part.Face(w1)
                elif (dxfCreateDraft or dxfCreateSketch) and (not curves) and (not forceShape):
                    ob = Draft.makeWire(verts)
                    ob.Closed = polyline.closed
                    return ob
                else:
                    if polyline.closed and dxfFillMode:
                        w = Part.Wire(edges)
                        return(Part.Face(w))
                    else:
                        return Part.Wire(edges)
            except Part.OCCError:
                warn(polyline,num)
    return None

def drawArc(arc,forceShape=False):
    "returns a Part shape from a dxf arc"
    v=vec(arc.loc)
    firstangle=round(arc.start_angle,prec())
    lastangle=round(arc.end_angle,prec())
    circle=Part.Circle()
    circle.Center=v
    circle.Radius=vec(arc.radius)
    try:
        if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
            pl = FreeCAD.Placement()
            pl.move(v)
            return Draft.makeCircle(arc.radius,pl,False,firstangle,lastangle)
        else:
            return circle.toShape(math.radians(firstangle),math.radians(lastangle))
    except Part.OCCError:
        warn(arc)
    return None

def drawCircle(circle,forceShape=False):
    "returns a Part shape from a dxf circle"
    v = vec(circle.loc)
    curve = Part.Circle()
    curve.Radius = vec(circle.radius)
    curve.Center = v
    try:
        if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
            pl = FreeCAD.Placement()
            pl.move(v)
            return Draft.makeCircle(circle.radius,pl)
        else:
            return curve.toShape()
    except Part.OCCError:
        warn(circle)
    return None

def drawEllipse(ellipse,forceShape=False):
    "returns a Part shape from a dxf arc"
    try:
        c = vec(ellipse.loc)
        start = round(ellipse.start_angle,prec())
        end = round(ellipse.end_angle,prec())
        majv = vec(ellipse.major)
        majr = majv.Length
        minr = majr*ellipse.ratio
        el = Part.Ellipse(vec((0,0,0)),majr,minr)
        x = majv.normalize()
        z = vec(ellipse.extrusion).normalize()
        y = z.cross(x)
        m = DraftVecUtils.getPlaneRotation(x,y,z)
        pl = FreeCAD.Placement(m)
        pl.move(c)
        if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
            if (start != 0.0) or ((end != 0.0) or (end != round(math.pi/2,prec()))):
                shape = el.toShape(start,end)
                shape.Placement = pl
                return shape
            else:
                return Draft.makeEllipse(majr,minr,pl)
        else:
            shape = el.toShape(start,end)
            shape.Placement = pl
            return shape
    except Part.OCCError:
        warn(arc)
    return None

def drawFace(face):
    "returns a Part face from a list of points"
    pl = []
    for p in face.points:
        pl.append(vec(p))
    p1 = face.points[0]
    pl.append(vec(p1))
    try:
        pol = Part.makePolygon(pl)
        return Part.Face(pol)
    except Part.OCCError:
        warn(face)
    return None

def drawMesh(mesh,forceShape=False):
    "returns a Mesh from a dxf mesh"
    md = []
    if mesh.flags == 16:
        pts = mesh.points
        udim = rawValue(mesh,71)
        vdim = rawValue(mesh,72)
        for u in range(udim-1):
            for v in range(vdim-1):
                b = u+v*udim
                p1 = pts[b]
                p2 = pts[b+1]
                p3 = pts[b+udim]
                p4 = pts[b+udim+1]
                md.append([p1,p2,p4])
                md.append([p1,p4,p3])
    elif mesh.flags == 64:
        pts = []
        fcs = []
        for p in mesh.points:
            if p.flags == 192:
                pts.append(p)
            elif p.flags == 128:
                fcs.append(p)
        #print("Creating polyface with",len(pts),"points and",len(fcs),"facets")
        for f in fcs:
            p1 = pts[abs(rawValue(f,71))-1]
            p2 = pts[abs(rawValue(f,72))-1]
            p3 = pts[abs(rawValue(f,73))-1]
            md.append([p1,p2,p3])
            if rawValue(f,74) != None:
                p4 = pts[abs(rawValue(f,74))-1]
                md.append([p1,p3,p4])
    try:
        m = Mesh.Mesh(md)
        if forceShape:
            s = Part.Shape()
            s.makeShapeFromMesh(m.Topology,1)
            s = s.removeSplitter()
            m = s
    except FreeCAD.Base.FreeCADError:
        warn(mesh)
    else:
        return m
    return None

def drawSolid(solid):
    "returns a Part shape from a dxf solid"
    p4 = None
    p1x = rawValue(solid,10)
    p1y = rawValue(solid,20)
    p1z = rawValue(solid,30) or 0
    p2x = rawValue(solid,11)
    p2y = rawValue(solid,21)
    p2z = rawValue(solid,31) or p1z
    p3x = rawValue(solid,12)
    p3y = rawValue(solid,22)
    p3z = rawValue(solid,32) or p1z
    p4x = rawValue(solid,13)
    p4y = rawValue(solid,23)
    p4z = rawValue(solid,33) or p1z
    p1 = FreeCAD.Vector(p1x,p1y,p1z)
    p2 = FreeCAD.Vector(p2x,p2y,p2z)
    p3 = FreeCAD.Vector(p3x,p3y,p3z)
    if p4x != None: p4 = FreeCAD.Vector(p4x,p4y,p4z)
    if p4 and (p4 != p3) and (p4 != p2) and (p4 != p1):
        try:
            return Part.Face(Part.makePolygon([p1,p2,p4,p3,p1]))
        except Part.OCCError:
            warn(solid)
    else:
        try:
            return Part.Face(Part.makePolygon([p1,p2,p3,p1]))
        except Part.OCCError:
            warn(solid)
    return None

def drawSplineIterpolation(verts,closed=False,forceShape=False,\
        alwaysDiscretize=False):
    if (dxfCreateDraft or dxfCreateSketch) and (not forceShape):
        if dxfDiscretizeCurves or alwaysDiscretize:
            ob = Draft.makeWire(verts)
        else:
            ob = Draft.makeBSpline(verts)
        ob.Closed = closed
        return ob
    else:
        if dxfDiscretizeCurves or alwaysDiscretize:
            sh = Part.makePolygon(verts+[verts[0]])
        else:
            sp = Part.BSplineCurve()
            # print(knots)
            sp.interpolate(verts)
            sh = Part.Wire(sp.toShape())
        if closed and dxfFillMode:
            return Part.Face(sh)
        else:
            return sh

def drawSplineOld(spline,forceShape=False):
    "returns a Part Shape from a dxf spline"
    flag = rawValue(spline,70)
    if flag == 1:
        closed = True
    else:
        closed = False
    verts = []
    knots = []
    for dline in spline.data:
        if dline[0] == 10:
            cp = [dline[1]]
        elif dline[0] == 20:
            cp.append(dline[1])
        elif dline[0] == 30:
            cp.append(dline[1])
            pt = vec(cp)
            if verts:
                if pt != verts[-1]:
                    verts.append(pt)
            else:
                verts.append(pt)
        elif dline[0] == 40:
            knots.append(dline[1])
    try:
        return drawSplineIterpolation(verts,closed,forceShape)
    except Part.OCCError:
        warn(spline)
    return None

def drawSpline(spline,forceShape=False):
    """returns a Part Shape from a dxf spline
as there is currently no Draft premitive to handle splines the result is a
non-parametric curve"""
    flags = rawValue(spline,70)
    closed   = (flags &  1) != 0
    periodic = (flags &  2) != 0 and False # workaround
    rational = (flags &  4) != 0
    planar   = (flags &  8) != 0
    linear   = (flags & 16) != 0
    degree     = rawValue(spline,71)
    nbknots    = rawValue(spline,72) or 0
    nbcontrolp = rawValue(spline,73) or 0
    nbfitp     = rawValue(spline,74) or 0
    knots = []
    weights = []
    controlpoints = []
    fitpoints = []
    # parse the knots and points
    dataremain = spline.data[:]
    while len(dataremain) >0:
        groupnumber = dataremain[0][0]
        if groupnumber == 40: #knot
            knots.append(dataremain[0][1])
            dataremain = dataremain[1:]
        elif groupnumber == 41: #weight
            weights.append(dataremain[0][1])
            dataremain = dataremain[1:]
        elif groupnumber in (10,11): # control or fit point
            x = dataremain[0][1]
            if dataremain[1][0] in (20,21):
                y=dataremain[1][1]
                if dataremain[2][0] in (30,31):
                    z=dataremain[2][1]
                    dataremain = dataremain[3:]
                else:
                    z=0.0
                    dataremain = dataremain[2:]
            else:
                y=0.0
                dataremain = dataremain[1:]
            v = vec([x,y,z])
            if groupnumber == 10:
                controlpoints.append(v)
            elif groupnumber == 11:
                fitpoints.append(v)
        else:
            dataremain = dataremain[1:]
            #print groupnumber #debug

    if nbknots != len(knots):
        raise ValueError('Wrong number of knots')
    if nbcontrolp != len(controlpoints):
        raise ValueError('Wrong number of control points')
    if nbfitp != len(fitpoints):
        raise ValueError('Wrong number of fit points')
    if rational == all((w == 1.0 or w is None) for w in weights):
        raise ValueError('inconsistant rational flag')
    if len(weights) == 0:
        weights = None
    elif len(weights) != len(controlpoints):
        raise ValueError('Wrong number of weights')

    # build knotvector and multvector
    # this means to remove duplicate knots
    multvector=[]
    knotvector=[]
    mult=0
    previousknot=None
    for knotvalue in knots:
        if knotvalue == previousknot:
            mult += 1
        else:
            if mult > 0:
                multvector.append(mult)
            mult = 1
            previousknot = knotvalue
            knotvector.append(knotvalue)
    multvector.append(mult)
    # check if the multiplicities are valid
    innermults = multvector[:] if periodic else multvector[1:-1]
    if any(m>degree for m in innermults): #invalid
        if all(m == degree+1 for m in multvector):
            if not forceShape and weights is None:
                points=controlpoints[:]
                del points[degree+1::degree+1]
                return Draft.makeBezCurve(points,Degree=degree)
            else:
                poles=controlpoints[:]
                edges=[]
                while len(poles) >= degree+1:
                    #bezier segments
                    bzseg=Part.BezierCurve()
                    bzseg.increase(degree)
                    bzseg.setPoles(poles[0:degree+1])
                    poles=poles[degree+1:]
                    if weights is not None:
                        bzseg.setWeights(weights[0:degree+1])
                        weights=weights[degree+1:]
                    edges.append(bzseg.toShape())
                return Part.Wire(edges)
        else:
            warn('polygon fallback on %s' %spline)
            return drawSplineIterpolation(controlpoints,closed=closed,\
                forceShape=forceShape,alwaysDiscretize=True)
    if fitpoints and not(controlpoints):
        return drawSplineIterpolation(fitpoints,closed=closed,forceShape=forceShape)
    try:
        bspline=Part.BSplineCurve()
        bspline.buildFromPolesMultsKnots(poles=controlpoints,mults=multvector,\
                knots=knotvector,degree=degree,periodic=periodic,\
                weights=weights)
        return bspline.toShape()
    except Part.OCCError:
        warn(spline)
    return None

def drawBlock(blockref,num=None,createObject=False):
    "returns a shape from a dxf block reference"
    if not dxfStarBlocks:
        if blockref.name[0] == '*':
            return None
    if len(blockref.entities.data) == 0:
        print("skipping empty block ",blockref.name)
        return None
    #print("creating block ", blockref.name, " containing ", len(blockref.entities.data), " entities")
    shapes = []
    for line in blockref.entities.get_type('line'):
        s = drawLine(line,forceShape=True)
        if s: shapes.append(s)
    for polyline in blockref.entities.get_type('polyline'):
        if hasattr(polyline,"flags") and polyline.flags in [16,64]:
            s = drawMesh(polyline,forceShape=True)
        else:
            s = drawPolyline(polyline,forceShape=True)
        if s: shapes.append(s)
    for polyline in blockref.entities.get_type('lwpolyline'):
        s = drawPolyline(polyline,forceShape=True)
        if s: shapes.append(s)
    for arc in blockref.entities.get_type('arc'):
        s = drawArc(arc,forceShape=True)
        if s: shapes.append(s)
    for circle in blockref.entities.get_type('circle'):
        s = drawCircle(circle,forceShape=True)
        if s: shapes.append(s)
    for insert in blockref.entities.get_type('insert'):
        #print("insert ",insert," in block ",insert.block[0])
        if dxfStarBlocks or insert.block[0] != '*':
            s = drawInsert(insert)
            if s: shapes.append(s)
    for solid in blockref.entities.get_type('solid'):
        s = drawSolid(solid)
        if s: shapes.append(s)
    for spline in blockref.entities.get_type('spline'):
        s = drawSpline(spline,forceShape=True)
        if s: shapes.append(s)
    for text in blockref.entities.get_type('text'):
        if dxfImportTexts:
             if dxfImportLayouts or (not rawValue(text,67)):
                addText(text)
    for text in blockref.entities.get_type('mtext'):
        if dxfImportTexts:
             if dxfImportLayouts or (not rawValue(text,67)):
                print("adding block text",text.value, " from ",blockref)
                addText(text)
    try: shape = Part.makeCompound(shapes)
    except Part.OCCError: warn(blockref)
    if shape:
        blockshapes[blockref.name]=shape
        if createObject:
            newob=doc.addObject("Part::Feature",blockref.name)
            newob.Shape = shape
            blockobjects[blockref.name] = newob
            return newob
        return shape
    return None

def drawInsert(insert,num=None,clone=False):
    if dxfImportTexts:
        attrs = attribs(insert)
        for a in attrs:
            addText(a,attrib=True)
    if clone:
        if insert.block in blockobjects:
            newob = Draft.clone(blockobjects[insert.block])
            tsf = FreeCAD.Matrix()
            rot = math.radians(insert.rotation)
            pos = vec(insert.loc)
            tsf.move(pos)
            tsf.rotateZ(rot)
            sc = insert.scale
            sc = vec([sc[0],sc[1],0])
            newob.Placement = FreeCAD.Placement(tsf)
            newob.Scale = sc
            return newob
        else:
            shape = None
    else:
        if insert in blockshapes:
            shape = blockshapes[insert.block].copy()
        else:
            shape = None
            for b in drawing.blocks.data:
                if b.name == insert.block:
                    shape = drawBlock(b,num)
        if shape:
            pos = vec(insert.loc)
            rot = math.radians(insert.rotation)
            scale = insert.scale
            tsf = FreeCAD.Matrix()
            #tsf.scale(scale[0],scale[1],0) # for some reason z must be 0 to work
            tsf.rotateZ(rot)
            try:
                shape = shape.transformGeometry(tsf)
            except Part.OCCError:
                tsf.scale(scale[0],scale[1],0)
                try:
                    shape = shape.transformGeometry(tsf)
                except Part.OCCError:
                    print("importDXF: unable to apply insert transform:",tsf)
            shape.translate(pos)
            return shape
    return None

def drawLayerBlock(objlist):
    "draws a Draft block with the given shapes or objects"
    isObj = True
    for o in objlist:
        if isinstance(o,Part.Shape):
            isObj = False
    obj = None
    if (dxfCreateDraft or dxfCreateSketch) and isObj:
        try:
            obj = Draft.makeBlock(objlist)
        except Part.OCCError:
            pass
    else:
        try:
            obj = Part.makeCompound(objlist)
        except Part.OCCError:
            pass
    return obj

def attribs(insert):
    "checks if an insert has attributes, and returns the values if yes"
    atts = []
    if rawValue(insert,66) != 1: return []
    index = None
    for i in range(len(drawing.entities.data)):
        if drawing.entities.data[i] == insert:
            index = i
            break
    if index == None: return []
    j = index+1
    while True:
        ent = drawing.entities.data[j]
        if str(ent) == 'seqend':
            return atts
        elif str(ent) == 'attrib':
            atts.append(ent)
            j += 1

def addObject(shape,name="Shape",layer=None):
    "adds a new object to the document with passed arguments"
    if isinstance(shape,Part.Shape):
        newob=doc.addObject("Part::Feature",name)
        newob.Shape = shape
    else:
        newob = shape
    if layer:
        lay=locateLayer(layer)
        lay.addObject(newob)
    formatObject(newob)
    return newob

def addText(text,attrib=False):
    "adds a new text to the document"
    if attrib:
        lay = locateLayer(rawValue(text,8))
        val = rawValue(text,1)
        pos = vec([rawValue(text,10),rawValue(text,20),rawValue(text,30)])
        hgt = vec(rawValue(text,40))
    else:
        lay = locateLayer(text.layer)
        val = text.value
        pos = vec(text.loc)
        hgt = vec(text.height)
    if val:
        if attrib:
            name = "Attribute"
        else:
            name = "Text"
        val = deformat(val)
        # the following stores text as Latin1 in annotations, which
        # displays ok in coin texts, but causes errors later on.
        # better store as utf8 always.
        #try:
        #    val = val.decode("utf8").encode("Latin1")
        #except:
        #    try:
        #        val = val.encode("latin1")
        #    except:
        #        pass
        newob = Draft.makeText(val.split("\n"))
        lay.addObject(newob)
        rx = rawValue(text,11)
        ry = rawValue(text,21)
        rz = rawValue(text,31)
        xv = Vector(1,0,0)
        ax = Vector(0,0,1)
        if rx or ry or rz:
            xv = vec([rx,ry,rz])
            if not DraftVecUtils.isNull(xv):
                ax = (xv.cross(Vector(1,0,0))).negative()
                if DraftVecUtils.isNull(ax):
                    ax = Vector(0,0,1)
                ang = -math.degrees(DraftVecUtils.angle(xv,Vector(1,0,0),ax))
                Draft.rotate(newob,ang,axis=ax)
            if ax == Vector(0,0,-1): ax = Vector(0,0,1)
        elif hasattr(text,"rotation"):
            if text.rotation:
                Draft.rotate(newob,text.rotation)
        if attrib:
            attrot = rawValue(text,50)
            if attrot:
                Draft.rotate(newob,attrot)
        if gui and draftui and dxfUseStandardSize:
            fsize = draftui.fontsize
        else:
            fsize = float(hgt)*TEXTSCALING
        if hasattr(text,"alignment"):
            yv = ax.cross(xv)
            if text.alignment in [1,2,3]:
                sup = DraftVecUtils.scaleTo(yv,fsize/TEXTSCALING).negative()
                #print(ax,sup)
                pos = pos.add(sup)
            elif text.alignment in [4,5,6]:
                sup = DraftVecUtils.scaleTo(yv,fsize/(2*TEXTSCALING)).negative()
                pos = pos.add(sup)
        newob.Placement.Base = pos
        if gui:
            newob.ViewObject.FontSize = fsize
            if hasattr(text,"alignment"):
                if text.alignment in [2,5,8]:
                    newob.ViewObject.Justification = "Center"
                elif text.alignment in [3,6,9]:
                    newob.ViewObject.Justification = "Right"
            #newob.ViewObject.DisplayMode = "World"
            formatObject(newob,text)

def addToBlock(obj,layer):
    "adds given shape to the layer dict"
    if layer in layerBlocks:
        layerBlocks[layer].append(obj)
    else:
        layerBlocks[layer] = [obj]

def processdxf(document,filename,getShapes=False,reComputeFlag=True):
    "Recompute causes OpenSCAD import to loop, supply flag to make conditional"
    "this does the translation of the dxf contents into FreeCAD Part objects"
    global drawing # for debugging - so drawing is still accessible to python after the script ran
    if not dxfReader:
        getDXFlibs()
        readPreferences()
    FreeCAD.Console.PrintMessage("opening "+filename+"...\n")
    drawing = dxfReader.readDXF(filename)
    global layers
    layers = []
    global doc
    doc = document
    global blockshapes
    blockshapes = {}
    global blockobjects
    blockobjects = {}
    global badobjects
    badobjects = []
    global layerBlocks
    layerBlocks = {}
    sketch = None
    shapes = []

    # drawing lines

    lines = drawing.entities.get_type("line")
    if lines: FreeCAD.Console.PrintMessage("drawing "+str(len(lines))+" lines...\n")
    for line in lines:
        if dxfImportLayouts or (not rawValue(line,67)):
            shape = drawLine(line)
            if shape:
                if dxfCreateSketch:
                    if dxfMakeBlocks or dxfJoin:
                        if sketch:
                            shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
                        else:
                            shape = Draft.makeSketch(shape,autoconstraints=True)
                            sketch = shape
                    else:
                        shape = Draft.makeSketch(shape,autoconstraints=True)
                elif dxfJoin or getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                elif dxfMakeBlocks:
                    addToBlock(shape,line.layer)
                else:
                    newob = addObject(shape,"Line",line.layer)
                    if gui: formatObject(newob,line)

    # drawing polylines

    pls = drawing.entities.get_type("lwpolyline")
    pls.extend(drawing.entities.get_type("polyline"))
    polylines = []
    meshes = []
    for p in pls:
        if hasattr(p,"flags"):
            if p.flags in [16,64]:
                meshes.append(p)
            else:
                polylines.append(p)
        else:
            polylines.append(p)
    if polylines:
        FreeCAD.Console.PrintMessage("drawing "+str(len(polylines))+" polylines...\n")
    num = 0
    for polyline in polylines:
        if dxfImportLayouts or (not rawValue(polyline,67)):
            shape = drawPolyline(polyline,num)
            if shape:
                if dxfCreateSketch:
                    if isinstance(shape,Part.Shape):
                        t = FreeCAD.ActiveDocument.addObject("Part::Feature","Shape")
                        t.Shape = shape
                        shape = t
                    if dxfMakeBlocks or dxfJoin:
                        if sketch:
                            shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
                        else:
                            shape = Draft.makeSketch(shape,autoconstraints=True)
                            sketch = shape
                    else:
                        shape = Draft.makeSketch(shape,autoconstraints=True)
                elif dxfJoin or getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                elif dxfMakeBlocks:
                    addToBlock(shape,polyline.layer)
                else:
                    newob = addObject(shape,"Polyline",polyline.layer)
                    if gui: formatObject(newob,polyline)
            num += 1

    # drawing arcs

    arcs = drawing.entities.get_type("arc")
    if arcs: FreeCAD.Console.PrintMessage("drawing "+str(len(arcs))+" arcs...\n")
    for arc in arcs:
        if dxfImportLayouts or (not rawValue(arc,67)):
            shape = drawArc(arc)
            if shape:
                if dxfCreateSketch:
                    if dxfMakeBlocks or dxfJoin:
                        if sketch:
                            shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
                        else:
                            shape = Draft.makeSketch(shape,autoconstraints=True)
                            sketch = shape
                    else:
                        shape = Draft.makeSketch(shape,autoconstraints=True)
                elif dxfJoin or getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                elif dxfMakeBlocks:
                    addToBlock(shape,arc.layer)
                else:
                    newob = addObject(shape,"Arc",arc.layer)
                    if gui: formatObject(newob,arc)

    # joining lines, polylines and arcs if needed

    if dxfJoin and shapes:
        FreeCAD.Console.PrintMessage("Joining geometry...\n")
        edges = []
        for s in shapes:
            edges.extend(s.Edges)
        if len(edges) > (100):
            FreeCAD.Console.PrintMessage(str(len(edges))+" edges to join\n")
            if FreeCAD.GuiUp:
                from PySide import QtGui
                d = QtGui.QMessageBox()
                d.setText("Warning: High number of entities to join (>100)")
                d.setInformativeText("This might take a long time or even freeze your computer. Are you sure? You can also disable the \"join geometry\" setting in DXF import preferences")
                d.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
                d.setDefaultButton(QtGui.QMessageBox.Cancel)
                res = d.exec_()
                if res == QtGui.QMessageBox.Cancel:
                    FreeCAD.Console.PrintMessage("Aborted\n")
                    return
        shapes = DraftGeomUtils.findWires(edges)
        for s in shapes:
            newob = addObject(s)

    # drawing circles

    circles = drawing.entities.get_type("circle")
    if circles: FreeCAD.Console.PrintMessage("drawing "+str(len(circles))+" circles...\n")
    for circle in circles:
        if dxfImportLayouts or (not rawValue(circle,67)):
            shape = drawCircle(circle)
            if shape:
                if dxfCreateSketch:
                    if dxfMakeBlocks or dxfJoin:
                        if sketch:
                            shape = Draft.makeSketch(shape,autoconstraints=True,addTo=sketch)
                        else:
                            shape = Draft.makeSketch(shape,autoconstraints=True)
                            sketch = shape
                    else:
                        shape = Draft.makeSketch(shape,autoconstraints=True)
                elif dxfMakeBlocks:
                    addToBlock(shape,circle.layer)
                elif getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                else:
                    newob = addObject(shape,"Circle",circle.layer)
                    if gui: formatObject(newob,circle)

    # drawing solids

    solids = drawing.entities.get_type("solid")
    if solids: FreeCAD.Console.PrintMessage("drawing "+str(len(solids))+" solids...\n")
    for solid in solids:
        lay = rawValue(solid,8)
        if dxfImportLayouts or (not rawValue(solid,67)):
            shape = drawSolid(solid)
            if shape:
                if dxfMakeBlocks:
                    addToBlock(shape,lay)
                elif getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                else:
                    newob = addObject(shape,"Solid",lay)
                    if gui: formatObject(newob,solid)

    # drawing splines

    splines = drawing.entities.get_type("spline")
    if splines: FreeCAD.Console.PrintMessage("drawing "+str(len(splines))+" splines...\n")
    for spline in splines:
        lay = rawValue(spline,8)
        if dxfImportLayouts or (not rawValue(spline,67)):
            shape = drawSpline(spline)
            if shape:
                if dxfMakeBlocks:
                    addToBlock(shape,lay)
                elif getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                else:
                    newob = addObject(shape,"Spline",lay)
                    if gui: formatObject(newob,spline)

    # drawing ellipses

    ellipses = drawing.entities.get_type("ellipse")
    if ellipses: FreeCAD.Console.PrintMessage("drawing "+str(len(ellipses))+" ellipses...\n")
    for ellipse in ellipses:
        lay = rawValue(ellipse,8)
        if dxfImportLayouts or (not rawValue(ellipse,67)):
            shape = drawEllipse(ellipse)
            if shape:
                if dxfMakeBlocks:
                    addToBlock(shape,lay)
                elif getShapes:
                    if isinstance(shape,Part.Shape):
                        shapes.append(shape)
                    else:
                        shapes.append(shape.Shape)
                else:
                    newob = addObject(shape,"Ellipse",lay)
                    if gui: formatObject(newob,ellipse)

    # drawing texts

    if dxfImportTexts:
        texts = drawing.entities.get_type("mtext")
        texts.extend(drawing.entities.get_type("text"))
        if texts:
            FreeCAD.Console.PrintMessage("drawing "+str(len(texts))+" texts...\n")
        for text in texts:
            if dxfImportLayouts or (not rawValue(text,67)):
                addText(text)

    else: FreeCAD.Console.PrintMessage("skipping texts...\n")

    # drawing 3D objects

    faces3d = drawing.entities.get_type("3dface")
    if faces3d: FreeCAD.Console.PrintMessage("drawing "+str(len(faces3d))+" 3dfaces...\n")
    for face3d in faces3d:
        shape = drawFace(face3d)
        if shape:
            if getShapes:
                if isinstance(shape,Part.Shape):
                    shapes.append(shape)
                else:
                    shapes.append(shape.Shape)
            else:
                newob = addObject(shape,"Face",face3d.layer)
                if gui: formatObject(newob,face3d)
    if meshes: FreeCAD.Console.PrintMessage("drawing "+str(len(meshes))+" 3dmeshes...\n")
    for mesh in meshes:
        me = drawMesh(mesh)
        if me:
            newob = doc.addObject("Mesh::Feature","Mesh")
            lay = locateLayer(rawValue(mesh,8))
            lay.addObject(newob)
            newob.Mesh = me
            if gui: formatObject(newob,mesh)

    # end of shape-based objects, return if we are just getting shapes

    if getShapes and shapes:
        return(shapes)

    # drawing dims

    if dxfImportTexts:
        dims = drawing.entities.get_type("dimension")
        FreeCAD.Console.PrintMessage("drawing "+str(len(dims))+" dimensions...\n")
        for dim in dims:
            if dxfImportLayouts or (not rawValue(dim,67)):
                try:
                    layer = rawValue(dim,8)
                    if rawValue(dim,15) != None:
                        # this is a radial or diameter dimension
                        #x1 = float(rawValue(dim,11))
                        #y1 = float(rawValue(dim,21))
                        #z1 = float(rawValue(dim,31))
                        x2 = float(rawValue(dim,10))
                        y2 = float(rawValue(dim,20))
                        z2 = float(rawValue(dim,30))
                        x3 = float(rawValue(dim,15))
                        y3 = float(rawValue(dim,25))
                        z3 = float(rawValue(dim,35))
                        x1 = x2
                        y1 = y2
                        z1 = z2
                    else:
                        x1 = float(rawValue(dim,10))
                        y1 = float(rawValue(dim,20))
                        z1 = float(rawValue(dim,30))
                        x2 = float(rawValue(dim,13))
                        y2 = float(rawValue(dim,23))
                        z2 = float(rawValue(dim,33))
                        x3 = float(rawValue(dim,14))
                        y3 = float(rawValue(dim,24))
                        z3 = float(rawValue(dim,34))
                    d = rawValue(dim,70)
                    if d: align = int(d)
                    else: align = 0
                    d = rawValue(dim,50)
                    if d: angle = float(d)
                    else: angle = 0
                except (ValueError,TypeError):
                    warn(dim)
                else:
                    lay=locateLayer(layer)
                    pt = vec([x1,y1,z1])
                    p1 = vec([x2,y2,z2])
                    p2 = vec([x3,y3,z3])
                    if align >= 128:
                        align -= 128
                    elif align >= 64:
                        align -= 64
                    elif align >= 32:
                        align -= 32
                    if align == 0:
                        if angle in [0,180]:
                            p2 = vec([x3,y2,z2])
                        elif angle in [90,270]:
                            p2 = vec([x2,y3,z2])
                    newob = doc.addObject("App::FeaturePython","Dimension")
                    lay.addObject(newob)
                    _Dimension(newob)
                    _ViewProviderDimension(newob.ViewObject)
                    newob.Start = p1
                    newob.End = p2
                    newob.Dimline = pt
                    if gui:
                        dim.layer = layer
                        dim.color_index = 256
                        formatObject (newob,dim)
                        if dxfUseStandardSize and draftui:
                            newob.ViewObject.FontSize = draftui.fontsize
                        else:
                            st = rawValue(dim,3)
                            size = getdimheight(st) or 1
                            newob.ViewObject.FontSize = float(size)*TEXTSCALING
    else:
        FreeCAD.Console.PrintMessage("skipping dimensions...\n")

    # drawing points

    if dxfImportPoints:
        points = drawing.entities.get_type("point")
        if points: FreeCAD.Console.PrintMessage("drawing "+str(len(points))+" points...\n")
        for point in points:
                x = vec(rawValue(point,10))
                y = vec(rawValue(point,20))
                z = vec(rawValue(point,30))
                lay = rawValue(point,8)
                if dxfImportLayouts or (not rawValue(point,67)):
                    if dxfMakeBlocks:
                        shape = Part.Vertex(x,y,z)
                        addToBlock(shape,lay)
                    else:
                        newob = Draft.makePoint(x,y,z)
                        lay = locateLayer(lay)
                        lay.addObject(newob)
                        if gui:
                            formatObject(newob,point)
    else:
        FreeCAD.Console.PrintMessage("skipping points...\n")

    # drawing leaders

    if dxfImportTexts:
        leaders = drawing.entities.get_type("leader")
        if leaders:
            FreeCAD.Console.PrintMessage("drawing "+str(len(leaders))+" leaders...\n")
        for leader in leaders:
            if dxfImportLayouts or (not rawValue(leader,67)):
                points = getMultiplePoints(leader)
                newob = Draft.makeWire(points)
                lay = locateLayer(rawValue(leader,8))
                lay.addObject(newob)
                if gui:
                    newob.ViewObject.EndArrow = True
                    formatObject(newob,leader)
    else:
        FreeCAD.Console.PrintMessage("skipping leaders...\n")

    # drawing hatches

    if dxfImportHatches:
        hatches = drawing.entities.get_type("hatch")
        if hatches:
            FreeCAD.Console.PrintMessage("drawing "+str(len(hatches))+" hatches...\n")
        for hatch in hatches:
            if dxfImportLayouts or (not rawValue(hatch,67)):
                points = getMultiplePoints(hatch)
                if len(points) > 1:
                    lay = rawValue(hatch,8)
                    points = points[:-1]
                    newob = None
                    if dxfCreatePart or dxfMakeBlocks:
                        points.append(points[0])
                        s = Part.makePolygon(points)
                        if dxfMakeBlocks:
                            addToBlock(s,lay)
                        else:
                            newob = addObject(s,"Hatch",lay)
                            if gui:
                                formatObject(newob,hatch)
                    else:
                        newob = Draft.makeWire(points)
                        locateLayer(lay).addObject(newob)
                        if gui:
                            formatObject(newob,hatch)
    else:
        FreeCAD.Console.PrintMessage("skipping hatches...\n")

    # drawing blocks

    inserts = drawing.entities.get_type("insert")
    if not dxfStarBlocks:
        FreeCAD.Console.PrintMessage("skipping *blocks...\n")
        newinserts = []
        for i in inserts:
            if dxfImportLayouts or (not rawValue(i,67)):
                if i.block[0] != '*':
                    newinserts.append(i)
        inserts = newinserts
    if inserts:
        FreeCAD.Console.PrintMessage("drawing "+str(len(inserts))+" blocks...\n")
        blockrefs = drawing.blocks.data
        for ref in blockrefs:
            if dxfCreateDraft or dxfCreateSketch:
                drawBlock(ref,createObject=True)
            else:
                drawBlock(ref,createObject=False)
        num = 0
        for insert in inserts:
            if (dxfCreateDraft or dxfCreateSketch) and not(dxfMakeBlocks):
                shape = drawInsert(insert,num,clone=True)
            else:
                shape = drawInsert(insert,num)
            if shape:
                if dxfMakeBlocks:
                    addToBlock(shape,insert.layer)
                else:
                    newob = addObject(shape,"Block."+insert.block,insert.layer)
                    if gui: formatObject(newob,insert)
            num += 1

    # make blocks, if any

    if dxfMakeBlocks:
        print("creating layerblocks...")
        for k,l in layerBlocks.items():
            shape = drawLayerBlock(l)
            if shape:
                newob = addObject(shape,k)
    del layerBlocks

    # hide block objects, if any

    for k,o in blockobjects.items():
        if o.ViewObject:
            o.ViewObject.hide()
    del blockobjects

    # finishing

    print("done processing")

    if reComputeFlag :
       doc.recompute()
       print("recompute done")

    FreeCAD.Console.PrintMessage("successfully imported "+filename+"\n")
    if badobjects:
        print("dxf: ",len(badobjects)," objects were not imported")
    del doc
    del blockshapes

def warn(dxfobject,num=None):
    "outputs a warning if a dxf object couldn't be imported"
    print("dxf: couldn't import ", dxfobject, " (",num,")")
    badobjects.append(dxfobject)

def open(filename):
    "called when freecad opens a file."
    import sys
    readPreferences()
    if dxfUseLegacyImporter:
        getDXFlibs()
        if dxfReader:
            docname = os.path.splitext(os.path.basename(filename))[0]
            if sys.version_info.major < 3:
                if isinstance(docname,unicode): 
                    #workaround since newDocument currently can't handle unicode filenames
                    docname = docname.encode(sys.getfilesystemencoding())
            doc = FreeCAD.newDocument(docname)
            doc.Label = decodeName(docname)
            processdxf(doc,filename)
            return doc
        else:
            errorDXFLib(gui)
    else:
        docname = os.path.splitext(os.path.basename(filename))[0]
        if sys.version_info.major < 3:
            if isinstance(docname,unicode): 
                #workaround since newDocument currently can't handle unicode filenames
                docname = docname.encode(sys.getfilesystemencoding())
        doc = FreeCAD.newDocument(docname)
        doc.Label = decodeName(docname)
        FreeCAD.setActiveDocument(doc.Name)
        import Import
        Import.readDXF(filename)

def insert(filename,docname):
    "called when freecad imports a file"
    readPreferences()
    try:
        doc=FreeCAD.getDocument(docname)
    except NameError:
        doc=FreeCAD.newDocument(docname)
    FreeCAD.setActiveDocument(docname)
    if dxfUseLegacyImporter:
        getDXFlibs()
        if dxfReader:
            groupname = os.path.splitext(os.path.basename(filename))[0]
            if sys.version_info.major < 3:
                if isinstance(groupname,unicode): 
                    #workaround since newDocument currently can't handle unicode filenames
                    groupname = groupname.encode(sys.getfilesystemencoding())
            importgroup = doc.addObject("App::DocumentObjectGroup",groupname)
            importgroup.Label = decodeName(groupname)
            processdxf(doc,filename)
            for l in layers:
                importgroup.addObject(l)
        else:
            errorDXFLib(gui)
    else:
        import Import
        Import.readDXF(filename)

def getShapes(filename):
    "reads a dxf file and returns a list of shapes from its contents"
    if dxfReader:
        return processdxf(None,filename,getShapes=True)


# EXPORT ########################################################################

def projectShape(shape,direction,tess=None):
    import Drawing
    edges = []
    try:
        groups = Drawing.projectEx(shape,direction)
    except Part.OCCError:
        print("unable to project shape on direction ",direction)
        return shape
    else:
        for g in groups[0:5]:
            if g:
                edges.append(g)
        #return DraftGeomUtils.cleanProjection(Part.makeCompound(edges))
        if tess:
            return DraftGeomUtils.cleanProjection(Part.makeCompound(edges),tess[0],tess[1])
        else:
            return Part.makeCompound(edges)
            #return DraftGeomUtils.cleanProjection(Part.makeCompound(edges))

def getArcData(edge):
    "returns center, radius, start and end angles of a circle-based edge"
    ce = edge.Curve.Center
    radius = edge.Curve.Radius
    if len(edge.Vertexes) == 1:
        # closed circle
        return DraftVecUtils.tup(ce), radius, 0, 0
    else:
        # new method: recalculate ourselves - cannot trust edge.Curve.Axis or XAxis
        p1 = edge.Vertexes[0].Point
        p2 = edge.Vertexes[-1].Point
        v1 = p1.sub(ce)
        v2 = p2.sub(ce)
        #print v1.cross(v2)
        #print edge.Curve.Axis
        #print p1
        #print p2
        # we can use Z check since arcs getting here will ALWAYS be in XY plane
        # Z can be 0 if the arc is 180 deg
        #if (v1.cross(v2).z >= 0) or (edge.Curve.Axis.z > 0):
        if edge.Curve.Axis.z > 0:
            #clockwise
            ang1 = -DraftVecUtils.angle(v1)
            ang2 = -DraftVecUtils.angle(v2)
        else:
            #counterclockwise
            ang2 = -DraftVecUtils.angle(v1)
            ang1 = -DraftVecUtils.angle(v2)

        # obsolete method - fails a lot
        #if round(edge.Curve.Axis.dot(FreeCAD.Vector(0,0,1))) == 1:
        #    ang1,ang2=edge.ParameterRange
        #else:
        #    ang2,ang1=edge.ParameterRange
        #if edge.Curve.XAxis != FreeCAD.Vector(1,0,0):
        #    ang1 -= DraftVecUtils.angle(edge.Curve.XAxis)
        #    ang2 -= DraftVecUtils.angle(edge.Curve.XAxis)

        return DraftVecUtils.tup(ce), radius, math.degrees(ang1),\
                math.degrees(ang2)

def getSplineSegs(edge):
    "returns an array of vectors from a Spline or Bezier edge"
    params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
    seglength = params.GetFloat("maxsegmentlength",5.0)
    points = []
    if seglength == 0:
        points.append(edge.Vertexes[0].Point)
        points.append(edge.Vertexes[-1].Point)
    else:
        points.append(edge.valueAt(edge.FirstParameter))
        if (edge.Length > seglength):
            nbsegs = int(math.ceil(edge.Length/seglength))
            step = (edge.LastParameter-edge.FirstParameter)/nbsegs
            for nv in range(1,nbsegs):
                #print("value at",nv*step,"=",edge.valueAt(nv*step))
                v = edge.valueAt(edge.FirstParameter+(nv*step))
                points.append(v)
        points.append(edge.valueAt(edge.LastParameter))
    return points

def getWire(wire,nospline=False,lw=True,asis=False):
    "returns an array of dxf-ready points and bulges from a wire"
    def fmt(v,b=0.0):
        if lw:
            # LWpolyline format
            return (v.x,v.y,v.z,None,None,b)
        else:
            # Polyline format
            return ((v.x,v.y,v.z),None,[None,None],b)
    points = []
    if asis:
        points = [fmt(v.Point) for v in wire.OrderedVertexes]
    else:
        edges = Part.__sortEdges__(wire.Edges)
        # print("processing wire ",wire.Edges)
        for edge in edges:
            v1 = edge.Vertexes[0].Point
            if DraftGeomUtils.geomType(edge) == "Circle":
                # polyline bulge -> negative makes the arc go clockwise
                angle = edge.LastParameter-edge.FirstParameter
                bul = math.tan(angle/4)
                #if cross1[2] < 0:
                    # polyline bulge -> negative makes the arc go clockwise
                    #bul = -bul
                if edge.Curve.Axis.dot(FreeCAD.Vector(0,0,1)) < 0:
                    bul = -bul
                points.append(fmt(v1,bul))
            elif (DraftGeomUtils.geomType(edge) in ["BSplineCurve","BezierCurve","Ellipse"]) and (not nospline):
                spline = getSplineSegs(edge)
                spline.pop()
                for p in spline:
                    points.append(fmt(p))
            else:
                points.append(fmt(v1))
        if not DraftGeomUtils.isReallyClosed(wire):
            v = edges[-1].Vertexes[-1].Point
            points.append(fmt(v))
        # print("wire verts: ",points)
    return points

def getBlock(sh,obj,lwPoly=False):
    "returns a dxf block with the contents of the object"
    block = dxfLibrary.Block(name=obj.Name,layer=getStrGroup(obj))
    writeShape(sh,obj,block,lwPoly)
    return block

def writeShape(sh,ob,dxfobject,nospline=False,lwPoly=False,layer=None,color=None,asis=False):
    "writes the object's shape contents in the given dxf object"
    processededges = []
    if not layer:
        layer=getStrGroup(ob)
    if not color:
        color = getACI(ob)
    for wire in sh.Wires: # polylines
        if asis:
            edges = wire.Edges
        else:
            edges = Part.__sortEdges__(wire.Edges)
        for e in edges:
            processededges.append(e.hashCode())
        if (len(wire.Edges) == 1) and (DraftGeomUtils.geomType(wire.Edges[0]) == "Circle"):
            center, radius, ang1, ang2 = getArcData(wire.Edges[0])
            if center != None:
                if len(wire.Edges[0].Vertexes) == 1: # circle
                    dxfobject.append(dxfLibrary.Circle(center, radius,
                                                       color=color,
                                                       layer=layer))
                else: # arc
                    dxfobject.append(dxfLibrary.Arc(center, radius,
                                                    ang1, ang2, color=color,
                                                    layer=layer))
        else:
            if (lwPoly):
                if hasattr(dxfLibrary,"LwPolyLine"):
                    dxfobject.append(dxfLibrary.LwPolyLine(getWire(wire,nospline,asis=asis), [0.0,0.0],
                                                           int(DraftGeomUtils.isReallyClosed(wire)), color=color,
                                                           layer=layer))
                else:
                    FreeCAD.Console.PrintWarning("LwPolyLine support not found. Please delete dxfLibrary.py from your FreeCAD user directory to force auto-update\n")
            else :
                dxfobject.append(dxfLibrary.PolyLine(getWire(wire,nospline,lw=False,asis=asis), [0.0,0.0,0.0],
                                                     int(DraftGeomUtils.isReallyClosed(wire)), color=color,
                                                     layer=layer))
    if len(processededges) < len(sh.Edges): # lone edges
        loneedges = []
        for e in sh.Edges:
            if not(e.hashCode() in processededges): loneedges.append(e)
        # print("lone edges ",loneedges)
        for edge in loneedges:
            if (DraftGeomUtils.geomType(edge) in ["BSplineCurve","BezierCurve"]): # splines
                if (len(edge.Vertexes) == 1) and (edge.Curve.isClosed()) and (edge.Area > 0):
                    # special case: 1-vert closed spline, approximate as a circle
                    c = DraftGeomUtils.getCircleFromSpline(edge)
                    if c:
                        dxfobject.append(dxfLibrary.Circle(DraftVecUtils.tup(c.Curve.Center), c.Curve.Radius,
                                                           color=color,
                                                           layer=layer))
                else:
                    points = []
                    spline = getSplineSegs(edge)
                    for p in spline:
                        points.append(((p.x,p.y,p.z),None,[None,None],0.0))
                    dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0],
                                                         0, color=color,
                                                         layer=layer))
            elif DraftGeomUtils.geomType(edge) == "Circle": # curves
                center, radius, ang1, ang2 = getArcData(edge)
                if center != None:
                    if not isinstance(center,tuple):
                        center = DraftVecUtils.tup(center)
                    if len(edge.Vertexes) == 1: # circles
                        dxfobject.append(dxfLibrary.Circle(center, radius,
                                                           color=color,
                                                           layer=layer))
                    else : # arcs
                        dxfobject.append(dxfLibrary.Arc(center, radius,
                                                        ang1, ang2, color=getACI(ob),
                                                        layer=layer))
            elif DraftGeomUtils.geomType(edge) == "Ellipse": # ellipses:
                if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("DiscretizeEllipses",True):
                    points = []
                    spline = getSplineSegs(edge)
                    for p in spline:
                        points.append(((p.x,p.y,p.z),None,[None,None],0.0))
                    dxfobject.append(dxfLibrary.PolyLine(points, [0.0,0.0,0.0],
                                                         0, color=color,
                                                         layer=layer))
                else:
                    if hasattr(dxfLibrary,"Ellipse"):
                        center = DraftVecUtils.tup(edge.Curve.Center)
                        norm = DraftVecUtils.tup(edge.Curve.Axis)
                        start = edge.FirstParameter
                        end = edge.LastParameter
                        ax = edge.Curve.Focus1.sub(edge.Curve.Center)
                        major = DraftVecUtils.tup(DraftVecUtils.scaleTo(ax,edge.Curve.MajorRadius))
                        minor = edge.Curve.MinorRadius/edge.Curve.MajorRadius
                        # print("exporting ellipse: ",center,norm,start,end,major,minor)
                        dxfobject.append(dxfLibrary.Ellipse(center=center,majorAxis=major,normalAxis=norm,
                                                            minorAxisRatio=minor,startParameter=start,
                                                            endParameter=end,
                                                            color=color,
                                                            layer=layer))
                    else:
                        FreeCAD.Console.PrintWarning("Ellipses support not found. Please delete dxfLibrary.py from your FreeCAD user directory to force auto-update\n")
            else: # anything else is treated as lines
                if len(edge.Vertexes) > 1:
                    ve1=edge.Vertexes[0].Point
                    ve2=edge.Vertexes[1].Point
                    dxfobject.append(dxfLibrary.Line([DraftVecUtils.tup(ve1), DraftVecUtils.tup(ve2)],
                                                     color=color,
                                                     layer=layer))

def writeMesh(ob,dxf):
    "export a shape as a polyface mesh"
    meshdata = ob.Shape.tessellate(0.5)
    # print(meshdata)
    points = []
    faces = []
    for p in meshdata[0]:
        points.append([p.x,p.y,p.z])
    for f in meshdata[1]:
        faces.append([f[0]+1,f[1]+1,f[2]+1])
    # print(len(points),len(faces))
    dxf.append(dxfLibrary.PolyLine([points,faces], [0.0,0.0,0.0],
                                         64, color=getACI(ob),
                                         layer=getGroup(ob)))

def writePanelCut(ob,dxf,nospline,lwPoly,parent=None):
    if not hasattr(ob.Proxy,"outline"):
        ob.Proxy.execute(ob)
    if hasattr(ob.Proxy,"outline"):
        outl = ob.Proxy.outline.copy()
        tag = None
        if hasattr(ob.Proxy,"tag"):
            tag = ob.Proxy.tag
        if tag:
            tag = tag.copy()
            tag.Placement = ob.Placement.multiply(tag.Placement)
            if parent:
                tag.Placement = parent.Placement.multiply(tag.Placement)
        outl.Placement = ob.Placement.multiply(outl.Placement)
        if parent:
            outl.Placement = parent.Placement.multiply(outl.Placement)
        else:
            parent = ob
        if len(outl.Wires) > 1:
            # separate outline
            d = 0
            ow = None
            for w in outl.Wires:
                if w.BoundBox.DiagonalLength > d:
                    d = w.BoundBox.DiagonalLength
                    ow = w
            if ow:
                inl = Part.Compound([w for w in outl.Wires if w.hashCode() != ow.hashCode()])
                outl = ow
        else:
            inl = None
            outl = outl.Wires[0]

        writeShape(outl,parent,dxf,nospline,lwPoly,layer="Outlines",color=5)
        if inl:
            writeShape(inl,parent,dxf,nospline,lwPoly,layer="Cuts",color=4)
        if tag:
            writeShape(tag,parent,dxf,nospline,lwPoly,layer="Tags",color=2,asis=True)
            # sticky fonts can render very odd wires...
            #for w in tag.Edges:
            #    pts = [(v.X,v.Y,v.Z) for v in w.Vertexes]
            #    dxf.append(dxfLibrary.Line(pts,color=getACI(ob),layer="Tags"))

def getStrGroup(ob):
    "gets a string version of the group name"
    l = getGroup(ob)
    if sys.version_info.major < 3:
        if isinstance(l,unicode):
            # dxf R12 files are rather over-sensitive with utf8...
            try:
                import unicodedata
            except:
                # fallback
                return l.encode("ascii",errors="replace")
            else:
                # better encoding, replaces accented latin characters with corresponding ascii letter
                return ''.join((c for c in unicodedata.normalize('NFD', l) if unicodedata.category(c) != 'Mn')).encode("ascii",errors="replace")
    return l


def export(objectslist,filename,nospline=False,lwPoly=False):

    "called when freecad exports a file. If nospline=True, bsplines are exported as straight segs. lwPoly=True is for OpenSCAD DXF"

    readPreferences()
    getDXFlibs()
    if dxfLibrary:
        global exportList
        exportList = objectslist
        exportList = Draft.getGroupContents(exportList)
        nlist = []
        for ob in exportList:
            if Draft.getType(ob) == "AxisSystem":
                nlist.extend(ob.Axes)
            else:
                nlist.append(ob)
        exportList = nlist

        if (len(exportList) == 1) and (Draft.getType(exportList[0]) == "ArchSectionView"):

            # arch view: export it "as is"

            dxf = exportList[0].Proxy.getDXF()
            if dxf:
                f = open(filename,"w")
                f.write(dxf)
                f.close()

        elif (len(exportList) == 1) and (exportList[0].isDerivedFrom("Drawing::FeaturePage")):

            # page: special hack-export! (see below)

            exportPage(exportList[0],filename)

        elif (len(exportList) == 1) and (exportList[0].isDerivedFrom("TechDraw::DrawPage")):

            # page: special hack-export! (see below)

            exportPage(exportList[0],filename)

        else:

            # other cases, treat objects one by one

            dxf = dxfLibrary.Drawing()
            for ob in exportList:

                print("processing "+str(ob.Name))

                if Draft.getType(ob) == "PanelSheet":

                    if not hasattr(ob.Proxy,"sheetborder"):
                        ob.Proxy.execute(ob)
                    sb = ob.Proxy.sheetborder
                    if sb:
                        sb.Placement = ob.Placement
                        writeShape(sb,ob,dxf,nospline,lwPoly,layer="Sheets",color=1)
                    ss = ob.Proxy.sheettag
                    if ss:
                        ss.Placement = ob.Placement.multiply(ss.Placement)
                        writeShape(ss,ob,dxf,nospline,lwPoly,layer="SheetTags",color=1)
                    for subob in ob.Group:
                        if Draft.getType(subob) == "PanelCut":
                            writePanelCut(subob,dxf,nospline,lwPoly,parent=ob)
                        elif subob.isDerivedFrom("Part::Feature"):
                            shp = subob.Shape.copy()
                            shp.Placement = ob.Placement.multiply(shp.Placement)
                            writeShape(shp,ob,dxf,nospline,lwPoly,layer="Outlines",color=5)

                elif Draft.getType(ob) == "PanelCut":

                    writePanelCut(ob,dxf,nospline,lwPoly)

                elif Draft.getType(ob) == "Axis":

                    axes = ob.Proxy.getAxisData(ob)
                    if not axes:
                        continue
                    for ax in axes:
                        dxf.append(dxfLibrary.Line([tuple(ax[0]),tuple(ax[1])],color=getACI(ob),layer=getStrGroup(ob)))
                        p = ax[1]
                        h = 1
                        if FreeCAD.GuiUp:
                            vobj = ob.ViewObject
                            rad = vobj.BubbleSize.Value/2
                            n = 0
                            pos = ["Start"]
                            if hasattr(vobj,"BubblePosition"):
                                if vobj.BubblePosition == "Both":
                                    pos = ["Start","End"]
                                else:
                                    pos = [vobj.BubblePosition]
                            for p in pos:
                                if p == "Start":
                                    p1 = ax[0]
                                    p2 = ax[1]
                                else:
                                    p1 = ax[1]
                                    p2 = ax[0]
                                dv = p2.sub(p1)
                                dv.normalize()
                                center = p2.add(dv.scale(rad,rad,rad))
                                h = float(ob.ViewObject.FontSize)
                                dxf.append(dxfLibrary.Circle(center,rad,color=getACI(ob),layer=getStrGroup(ob)))
                                dxf.append(dxfLibrary.Text(ax[2],center,alignment=center,height=h,justifyhor=1,justifyver=2,color=getACI(ob),style='STANDARD',layer=getStrGroup(ob)))
                        else:
                            dxf.append(dxfLibrary.Text(ax[2],p,alignment=p,height=h,justifyhor=1,justifyver=2,color=getACI(ob),style='STANDARD',layer=getStrGroup(ob)))


                elif ob.isDerivedFrom("Part::Feature"):

                    tess = None
                    if hasattr(ob,"Tessellation"):
                        if ob.Tessellation:
                            tess = [ob.Tessellation,ob.SegmentLength]
                    if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("dxfmesh"):
                        sh = None
                        if not ob.Shape.isNull():
                            writeMesh(ob,dxf)
                    elif gui and FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetBool("dxfproject"):
                        direction = FreeCADGui.ActiveDocument.ActiveView.\
                                getViewDirection().multiply(-1)
                        sh = projectShape(ob.Shape,direction,tess)
                    else:
                        if ob.Shape.Volume > 0:
                            sh = projectShape(ob.Shape,Vector(0,0,1),tess)
                        else:
                            sh = ob.Shape
                    if sh:
                        if not sh.isNull():
                            if sh.ShapeType == 'Compound':
                                if (len(sh.Wires) == 1):
                                    # only one wire in this compound, no lone edge -> polyline
                                    if (len(sh.Wires[0].Edges) == len(sh.Edges)):
                                        writeShape(sh,ob,dxf,nospline,lwPoly)
                                    else:
                                        # 1 wire + lone edges -> block
                                        block = getBlock(sh,ob,lwPoly)
                                        dxf.blocks.append(block)
                                        dxf.append(dxfLibrary.Insert(name=ob.Name.upper(),
                                                                     color=getACI(ob),
                                                                     layer=getStrGroup(ob)))
                                else:
                                    # all other cases: block
                                    block = getBlock(sh,ob,lwPoly)
                                    dxf.blocks.append(block)
                                    dxf.append(dxfLibrary.Insert(name=ob.Name.upper(),
                                                                      color=getACI(ob),
                                                                      layer=getStrGroup(ob)))

                            else:
                                writeShape(sh,ob,dxf,nospline,lwPoly)

                elif Draft.getType(ob) == "Annotation":

                    # texts

                    # temporary - as dxfLibrary doesn't support mtexts well, we use several single-line texts
                    # well, anyway, at the moment, Draft only writes single-line texts, so...
                    for text in ob.LabelText:
                        point = DraftVecUtils.tup(FreeCAD.Vector(ob.Position.x,
                                                         ob.Position.y-ob.LabelText.index(text),
                                                         ob.Position.z))
                        if gui: height = float(ob.ViewObject.FontSize)
                        else: height = 1
                        dxf.append(dxfLibrary.Text(text,point,height=height,
                                                   color=getACI(ob,text=True),
                                                   style='STANDARD',
                                                   layer=getStrGroup(ob)))

                elif Draft.getType(ob) == "DraftText":

                    # texts

                    for text in ob.Text:
                        point = DraftVecUtils.tup(FreeCAD.Vector(ob.Placement.Base.x,
                                                         ob.Placement.Base.y-ob.Text.index(text),
                                                         ob.Placement.Base.z))
                        if gui: height = float(ob.ViewObject.FontSize)
                        else: height = 1
                        dxf.append(dxfLibrary.Text(text,point,height=height,
                                                   color=getACI(ob,text=True),
                                                   style='STANDARD',
                                                   layer=getStrGroup(ob)))

                elif Draft.getType(ob) == "Dimension":

                    p1 = DraftVecUtils.tup(ob.Start)
                    p2 = DraftVecUtils.tup(ob.End)
                    base = Part.LineSegment(ob.Start,ob.End).toShape()
                    proj = DraftGeomUtils.findDistance(ob.Dimline,base)
                    if not proj:
                        pbase = DraftVecUtils.tup(ob.End)
                    else:
                        pbase = DraftVecUtils.tup(ob.End.add(proj.negative()))
                    dxf.append(dxfLibrary.Dimension(pbase,p1,p2,color=getACI(ob),
                                                    layer=getStrGroup(ob)))

            if sys.version_info.major < 3:
                if isinstance(filename,unicode):
                    filename = filename.encode("utf8")
            dxf.saveas(filename)

        FreeCAD.Console.PrintMessage("successfully exported "+filename+"\r\n")

    else:

        errorDXFLib(gui)
        
class dxfcounter:
    def __init__(self):
        self.count = 10000 # this leaves 10000 entities for the template...
    def incr(self,matchobj):
        self.count += 1
        #print format(self.count,'02x')
        return format(self.count,'02x')

def exportPage(page,filename):
    "special export for pages"
    if hasattr(page.Template,"Template"): #techdraw
        template="" # not supported for now...
        views = page.Views
    else: #drawing
        template = os.path.splitext(page.Template)[0]+".dxf"
        views = page.Group
    if os.path.exists(template):
        f = pythonopen(template,"U")
        template = f.read()
        f.close()
        # find & replace editable texts
        f = pythonopen(page.Template,"rb")
        svgtemplate = f.read()
        f.close()
        editables = re.findall("freecad:editable=\"(.*?)\"",svgtemplate)
        values = page.EditableTexts
        for i in range(len(editables)):
            if len(values) > i:
                template = template.replace(editables[i],values[i])
    else:
        # dummy default template
        print("DXF version of the template not found. Creating a default empty template.")
        template = "999\nFreeCAD DXF exporter v"+FreeCAD.Version()[0]+"."+FreeCAD.Version()[1]+"-"+FreeCAD.Version()[2]+"\n"
        template += "0\nSECTION\n2\nHEADER\n9\n$ACADVER\n1\nAC1009\n0\nENDSEC\n"
        template += "0\nSECTION\n2\nBLOCKS\n999\n$blocks\n0\nENDSEC\n"
        template += "0\nSECTION\n2\nENTITIES\n999\n$entities\n0\nENDSEC\n"
        template += "0\nEOF"
    blocks = ""
    entities = ""
    r12 = False
    ver = re.findall("\$ACADVER\n.*?\n(.*?)\n",template)
    if ver:
        # at the moment this is not used. TODO: if r12, do not print ellipses or splines
        if ver[0].upper() in ["AC1009","AC1010","AC1011","AC1012","AC1013"]:
            r12 = True
    for view in views:
        b,e = getViewDXF(view)
        blocks += b
        entities += e
    if blocks:
        template = template.replace("999\n$blocks",blocks[:-1])
    if entities:
        template = template.replace("999\n$entities",entities[:-1])
    c = dxfcounter()
    pat = re.compile("(_handle_)")
    template = pat.sub(c.incr,template)
    f = pythonopen(filename,"wb")
    f.write(template)
    f.close()

def getViewBlock(geom,view,blockcount):
    insert = ""
    block = ""
    r = view.Rotation
    if r != 0: r = -r # fix rotation direction
    if not isinstance(geom,list): geom = [geom]
    for g in geom: # getDXF returns a list of entities
        if dxfExportBlocks:
            g = g.replace("sheet_layer\n","0\n6\nBYBLOCK\n62\n0\n5\n_handle_\n") # change layer and set color and ltype to BYBLOCK (0)
            block += "0\nBLOCK\n5\n_handle_\n100\nAcDbEntity\n8\n0\n100\nAcDbBlockBegin\n2\n"+view.Name+str(blockcount)+"\n70\n0\n10\n0\n20\n0\n3\n"+view.Name+str(blockcount)+"\n1\n\n"
            block += g
            block += "0\nENDBLK\n5\n_handle_\n100\nAcDbEntity\n8\n0\n100\nAcDbBlockEnd\n"
            insert += "0\nINSERT\n5\n_handle_\n8\n0\n6\nBYLAYER\n62\n256\n2\n"+view.Name+str(blockcount)
            insert += "\n10\n"+str(view.X)+"\n20\n"+str(-view.Y)
            insert += "\n30\n0\n41\n"+str(view.Scale)+"\n42\n"+str(view.Scale)+"\n43\n"+str(view.Scale)
            insert += "\n50\n"+str(r)+"\n"
            blockcount += 1
        else:
            g = g.replace("sheet_layer\n","0\n5\n_handle_\n") # change layer, add handle
            insert += g
    return block,insert,blockcount


def getViewDXF(view,blocks=True):
    "returns a DXF fragment from a Drawing View"
    block = ""
    insert = ""
    blockcount = 1

    if view.isDerivedFrom("App::DocumentObjectGroup"):
        for child in view.Group:
            b,e = getViewDXF(child)
            block += b
            insert += e

    elif view.isDerivedFrom("Drawing::FeatureViewPython"):
        if hasattr(view.Proxy,"getDXF"):
            geom = view.Proxy.getDXF(view)
            block,insert,blockcount = getViewBlock(geom,view,blockcount)

    elif view.isDerivedFrom("TechDraw::DrawViewDraft"):
        geom = Draft.getDXF(view)
        block,insert,blockcount = getViewBlock(geom,view,blockcount)
        
    elif view.isDerivedFrom("TechDraw::DrawViewArch"):
        import ArchSectionPlane
        geom = ArchSectionPlane.getDXF(view)
        block,insert,blockcount = getViewBlock(geom,view,blockcount)

    elif view.isDerivedFrom("Drawing::FeatureViewPart"):
        r = view.Rotation
        if r != 0: r = -r # fix rotation direction
        import Drawing
        proj = Drawing.projectToDXF(view.Source.Shape,view.Direction)
        if dxfExportBlocks:
            proj = proj.replace("sheet_layer\n","0\n6\nBYBLOCK\n62\n0\n5\n_handle_\n") # change layer and set color and ltype to BYBLOCK (0)
            block = "0\nBLOCK\n5\n_handle_\n100\nAcDbEntity\n8\n0\n100\nAcDbBlockBegin\n2\n"+view.Name+str(blockcount)+"\n70\n0\n10\n0\n20\n0\n3\n"+view.Name+str(blockcount)+"\n1\n\n"
            block += proj
            block += "0\nENDBLK\n5\n_handle_\n100\nAcDbEntity\n8\n0\n100\nAcDbBlockEnd\n"
            insert += "0\nINSERT\n5\n_handle_\n8\n0\n6\nBYLAYER\n62\n256\n2\n"+view.Name+str(blockcount)
            insert += "\n10\n"+str(view.X)+"\n20\n"+str(-view.Y)
            insert += "\n30\n0\n41\n"+str(view.Scale)+"\n42\n"+str(view.Scale)+"\n43\n"+str(view.Scale)
            insert += "\n50\n"+str(r)+"\n"
            blockcount += 1
        else:
            proj = proj.replace("sheet_layer\n","0\n5\n_handle_\n")
            insert += proj

    elif view.isDerivedFrom("Drawing::FeatureViewAnnotation"):
        r = view.Rotation
        if r != 0: r = -r # fix rotation direction
        insert ="0\nTEXT\n5\n_handle_\n8\n0\n100\nAcDbEntity\n100\nAcDbText\n5\n_handle_"
        insert += "\n10\n"+str(view.X)+"\n20\n"+str(-view.Y)
        insert += "\n30\n0\n40\n"+str(view.Scale/2)
        insert += "\n50\n"+str(r)
        insert += "\n1\n"+view.Text[0]+"\n"

    else:
        print("Unable to get DXF representation from view: ",view.Label)
    return block,insert


def exportPageLegacy(page,filename):
    "exports the given page the old way, by converting its SVG code to DXF with the Draft module"
    import importSVG
    tempdoc = importSVG.open(page.PageResult)
    tempobj = tempdoc.Objects
    export(tempobj,filename,nospline=True,lwPoly=False)
    FreeCAD.closeDocument(tempdoc.Name)


def readPreferences():
    # reading parameters
    p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
    if FreeCAD.GuiUp and p.GetBool("dxfShowDialog",False):
        import FreeCADGui
        FreeCADGui.showPreferences("Import-Export",2)
    global dxfCreatePart, dxfCreateDraft, dxfCreateSketch, dxfDiscretizeCurves, dxfStarBlocks
    global dxfMakeBlocks, dxfJoin, dxfRenderPolylineWidth, dxfImportTexts, dxfImportLayouts
    global dxfImportPoints, dxfImportHatches, dxfUseStandardSize, dxfGetColors, dxfUseDraftVisGroups
    global dxfFillMode, dxfBrightBackground, dxfDefaultColor, dxfUseLegacyImporter, dxfExportBlocks, dxfScaling
    dxfCreatePart = p.GetBool("dxfCreatePart",True)
    dxfCreateDraft = p.GetBool("dxfCreateDraft",False)
    dxfCreateSketch = p.GetBool("dxfCreateSketch",False)
    dxfDiscretizeCurves = p.GetBool("DiscretizeEllipses",True)
    dxfStarBlocks = p.GetBool("dxfstarblocks",False)
    dxfMakeBlocks = p.GetBool("groupLayers",False)
    dxfJoin = p.GetBool("joingeometry",False)
    dxfRenderPolylineWidth = p.GetBool("renderPolylineWidth",False)
    dxfImportTexts = p.GetBool("dxftext",False)
    dxfImportLayouts = p.GetBool("dxflayouts",False)
    dxfImportPoints = p.GetBool("dxfImportPoints",False)
    dxfImportHatches = p.GetBool("importDxfHatches",False)
    dxfUseStandardSize = p.GetBool("dxfStdSize",False)
    dxfGetColors = p.GetBool("dxfGetOriginalColors",False)
    dxfUseDraftVisGroups = p.GetBool("dxfUseDraftVisGroups",False)
    dxfFillMode = p.GetBool("fillmode",True)
    dxfUseLegacyImporter = p.GetBool("dxfUseLegacyImporter",False)
    dxfBrightBackground = isBrightBackground()
    dxfDefaultColor = getColor()
    dxfExportBlocks = p.GetBool("dxfExportBlocks",True)
    dxfScaling = p.GetFloat("dxfScaling",1.0)
