# Copyright (c) 2011-2022, Manfred Moitzi # License: MIT License from __future__ import annotations from dataclasses import dataclass DXF9 = "AC1004" DXF10 = "AC1006" DXF12 = "AC1009" DXF13 = "AC1012" DXF14 = "AC1014" DXF2000 = "AC1015" DXF2004 = "AC1018" DXF2007 = "AC1021" DXF2010 = "AC1024" DXF2013 = "AC1027" DXF2018 = "AC1032" acad_release = { DXF9: "R9", DXF10: "R10", DXF12: "R12", DXF13: "R13", DXF14: "R14", DXF2000: "R2000", DXF2004: "R2004", DXF2007: "R2007", DXF2010: "R2010", DXF2013: "R2013", DXF2018: "R2018", } acad_maint_ver = { DXF12: 0, DXF2000: 6, DXF2004: 0, DXF2007: 25, DXF2010: 6, DXF2013: 105, DXF2018: 4, } versions_supported_by_new = [ DXF12, DXF2000, DXF2004, DXF2007, DXF2010, DXF2013, DXF2018, ] versions_supported_by_save = versions_supported_by_new LATEST_DXF_VERSION = versions_supported_by_new[-1] acad_release_to_dxf_version = {acad: dxf for dxf, acad in acad_release.items()} class DXFError(Exception): """Base exception for all `ezdxf` exceptions. """ pass class InvalidGeoDataException(DXFError): pass class DXFStructureError(DXFError): pass class DXFLoadError(DXFError): pass class DXFAppDataError(DXFStructureError): pass class DXFXDataError(DXFStructureError): pass class DXFVersionError(DXFError): """Errors related to features not supported by the chosen DXF Version""" pass class DXFInternalEzdxfError(DXFError): """Indicates internal errors - should be fixed by mozman""" pass class DXFUnsupportedFeature(DXFError): """Indicates unsupported features for DXFEntities e.g. translation for ACIS data """ pass class DXFValueError(DXFError, ValueError): pass class DXFKeyError(DXFError, KeyError): pass class DXFAttributeError(DXFError, AttributeError): pass class DXFIndexError(DXFError, IndexError): pass class DXFTypeError(DXFError, TypeError): pass class DXFTableEntryError(DXFValueError): pass class DXFEncodingError(DXFError): pass class DXFDecodingError(DXFError): pass class DXFInvalidLineType(DXFValueError): pass class DXFBlockInUseError(DXFValueError): pass class DXFUndefinedBlockError(DXFKeyError): pass class DXFRenderError(DXFError): """Errors related to DXF "rendering" tasks. In this context "rendering" means creating the graphical representation of complex DXF entities by DXF primitives (LINE, TEXT, ...) e.g. for DIMENSION or LEADER entities. """ pass class DXFMissingDefinitionPoint(DXFRenderError): """Missing required definition points in the DIMENSION entity.""" def normalize_dxfversion(dxfversion: str, check_save=True) -> str: """Normalizes the DXF version string to "AC10xx".""" dxfversion = dxfversion.upper() dxfversion = acad_release_to_dxf_version.get(dxfversion, dxfversion) if check_save and dxfversion not in versions_supported_by_save: raise DXFVersionError(f"Invalid DXF version: {dxfversion}") return dxfversion MANAGED_SECTIONS = { "HEADER", "CLASSES", "TABLES", "BLOCKS", "ENTITIES", "OBJECTS", "ACDSDATA", } TABLE_NAMES_ACAD_ORDER = [ "VPORT", "LTYPE", "LAYER", "STYLE", "VIEW", "UCS", "APPID", "DIMSTYLE", "BLOCK_RECORD", ] DEFAULT_TEXT_STYLE = "Standard" DEFAULT_TEXT_FONT = "txt" APP_DATA_MARKER = 102 SUBCLASS_MARKER = 100 XDATA_MARKER = 1001 COMMENT_MARKER = 999 STRUCTURE_MARKER = 0 HEADER_VAR_MARKER = 9 ACAD_REACTORS = "{ACAD_REACTORS" ACAD_XDICTIONARY = "{ACAD_XDICTIONARY" XDICT_HANDLE_CODE = 360 REACTOR_HANDLE_CODE = 330 OWNER_CODE = 330 # https://help.autodesk.com/view/OARX/2018/ENU/?guid=GUID-2553CF98-44F6-4828-82DD-FE3BC7448113 MAX_STR_LEN = 255 # DXF R12 without line endings EXT_MAX_STR_LEN = 2049 # DXF R2000+ without line endings # Special tag codes for internal purpose: # -1 to -5 id reserved by AutoCAD for internal use, but this tags will never be # saved to file. # Same approach here, the following tags have to be converted/transformed into # normal tags before saved to file. COMPRESSED_TAGS = -10 # All color related constants are located in colors.py BYBLOCK = 0 BYLAYER = 256 BYOBJECT = 257 RED = 1 YELLOW = 2 GREEN = 3 CYAN = 4 BLUE = 5 MAGENTA = 6 BLACK = 7 WHITE = 7 # All transparency related constants are located in colors.py TRANSPARENCY_BYBLOCK = 0x01000000 LINEWEIGHT_BYLAYER = -1 LINEWEIGHT_BYBLOCK = -2 LINEWEIGHT_DEFAULT = -3 VALID_DXF_LINEWEIGHTS = ( 0, 5, 9, 13, 15, 18, 20, 25, 30, 35, 40, 50, 53, 60, 70, 80, 90, 100, 106, 120, 140, 158, 200, 211, ) MAX_VALID_LINEWEIGHT = VALID_DXF_LINEWEIGHTS[-1] VALID_DXF_LINEWEIGHT_VALUES = set(VALID_DXF_LINEWEIGHTS) | { LINEWEIGHT_DEFAULT, LINEWEIGHT_BYLAYER, LINEWEIGHT_BYBLOCK, } # Entity: Polyline, Polymesh # 70 flags POLYLINE_CLOSED = 1 POLYLINE_MESH_CLOSED_M_DIRECTION = POLYLINE_CLOSED POLYLINE_CURVE_FIT_VERTICES_ADDED = 2 POLYLINE_SPLINE_FIT_VERTICES_ADDED = 4 POLYLINE_3D_POLYLINE = 8 POLYLINE_3D_POLYMESH = 16 POLYLINE_MESH_CLOSED_N_DIRECTION = 32 POLYLINE_POLYFACE = 64 POLYLINE_GENERATE_LINETYPE_PATTERN = 128 # Entity: Polymesh # 75 surface smooth type POLYMESH_NO_SMOOTH = 0 POLYMESH_QUADRATIC_BSPLINE = 5 POLYMESH_CUBIC_BSPLINE = 6 POLYMESH_BEZIER_SURFACE = 8 # Entity: Vertex # 70 flags VERTEXNAMES = ("vtx0", "vtx1", "vtx2", "vtx3") VTX_EXTRA_VERTEX_CREATED = 1 # Extra vertex created by curve-fitting VTX_CURVE_FIT_TANGENT = 2 # Curve-fit tangent defined for this vertex. # A curve-fit tangent direction of 0 may be omitted from the DXF output, but is # significant if this bit is set. # 4 = unused, never set in dxf files VTX_SPLINE_VERTEX_CREATED = 8 # Spline vertex created by spline-fitting VTX_SPLINE_FRAME_CONTROL_POINT = 16 VTX_3D_POLYLINE_VERTEX = 32 VTX_3D_POLYGON_MESH_VERTEX = 64 VTX_3D_POLYFACE_MESH_VERTEX = 128 VERTEX_FLAGS = { "AcDb2dPolyline": 0, "AcDb3dPolyline": VTX_3D_POLYLINE_VERTEX, "AcDbPolygonMesh": VTX_3D_POLYGON_MESH_VERTEX, "AcDbPolyFaceMesh": VTX_3D_POLYGON_MESH_VERTEX | VTX_3D_POLYFACE_MESH_VERTEX, } POLYLINE_FLAGS = { "AcDb2dPolyline": 0, "AcDb3dPolyline": POLYLINE_3D_POLYLINE, "AcDbPolygonMesh": POLYLINE_3D_POLYMESH, "AcDbPolyFaceMesh": POLYLINE_POLYFACE, } # block-type flags (bit coded values, may be combined): # Entity: BLOCK # 70 flags # This is an anonymous block generated by hatching, associative dimensioning, # other internal operations, or an application: BLK_ANONYMOUS = 1 # This block has non-constant attribute definitions (this bit is not set if the # block has any attribute definitions that are constant, or has no attribute # definitions at all) BLK_NON_CONSTANT_ATTRIBUTES = 2 # This block is an external reference (xref): BLK_XREF = 4 # This block is an xref overlay: BLK_XREF_OVERLAY = 8 # This block is externally dependent: BLK_EXTERNAL = 16 # This is a resolved external reference, or dependent of an external reference # (ignored on input): BLK_RESOLVED = 32 # This definition is a referenced external reference (ignored on input): BLK_REFERENCED = 64 LWPOLYLINE_CLOSED = 1 LWPOLYLINE_PLINEGEN = 128 DEFAULT_TTF = "DejaVuSans.ttf" # TextHAlign enum LEFT = 0 CENTER = 1 RIGHT = 2 ALIGNED = 3 FIT = 5 BASELINE = 0 BOTTOM = 1 MIDDLE = 2 TOP = 3 MIRROR_X = 2 BACKWARD = MIRROR_X MIRROR_Y = 4 UPSIDE_DOWN = MIRROR_Y VERTICAL_STACKED = 4 # only stored in TextStyle.dxf.flags! # Special char and encodings used in TEXT, ATTRIB and ATTDEF: # "%%d" -> "°" SPECIAL_CHAR_ENCODING = { "c": "Ø", # alt-0216 "d": "°", # alt-0176 "p": "±", # alt-0177 } # Inline codes for strokes in TEXT, ATTRIB and ATTDEF # %%u underline # %%o overline # %%k strike through # Formatting will be applied until the same code appears again or the end # of line. # Special codes and formatting is case insensitive: d=D, u=U # MTextEntityAlignment enum MTEXT_TOP_LEFT = 1 MTEXT_TOP_CENTER = 2 MTEXT_TOP_RIGHT = 3 MTEXT_MIDDLE_LEFT = 4 MTEXT_MIDDLE_CENTER = 5 MTEXT_MIDDLE_RIGHT = 6 MTEXT_BOTTOM_LEFT = 7 MTEXT_BOTTOM_CENTER = 8 MTEXT_BOTTOM_RIGHT = 9 # MTextFlowDirection enum MTEXT_LEFT_TO_RIGHT = 1 MTEXT_TOP_TO_BOTTOM = 3 MTEXT_BY_STYLE = 5 # MTextLineSpacing enum MTEXT_AT_LEAST = 1 MTEXT_EXACT = 2 MTEXT_COLOR_INDEX = { "red": RED, "yellow": YELLOW, "green": GREEN, "cyan": CYAN, "blue": BLUE, "magenta": MAGENTA, "white": WHITE, } # MTextBackgroundColor enum MTEXT_BG_OFF = 0 MTEXT_BG_COLOR = 1 MTEXT_BG_WINDOW_COLOR = 2 MTEXT_BG_CANVAS_COLOR = 3 MTEXT_TEXT_FRAME = 16 CLOSED_SPLINE = 1 PERIODIC_SPLINE = 2 RATIONAL_SPLINE = 4 PLANAR_SPLINE = 8 LINEAR_SPLINE = 16 # Hatch constants HATCH_TYPE_USER_DEFINED = 0 HATCH_TYPE_PREDEFINED = 1 HATCH_TYPE_CUSTOM = 2 HATCH_PATTERN_TYPE = ["user-defined", "predefined", "custom"] ISLAND_DETECTION = ["nested", "outermost", "ignore"] HATCH_STYLE_NORMAL = 0 HATCH_STYLE_NESTED = 0 HATCH_STYLE_OUTERMOST = 1 HATCH_STYLE_IGNORE = 2 BOUNDARY_PATH_DEFAULT = 0 BOUNDARY_PATH_EXTERNAL = 1 BOUNDARY_PATH_POLYLINE = 2 BOUNDARY_PATH_DERIVED = 4 BOUNDARY_PATH_TEXTBOX = 8 BOUNDARY_PATH_OUTERMOST = 16 def boundary_path_flag_names(flags: int) -> list[str]: if flags == 0: return ["default"] types: list[str] = [] if flags & BOUNDARY_PATH_EXTERNAL: types.append("external") if flags & BOUNDARY_PATH_POLYLINE: types.append("polyline") if flags & BOUNDARY_PATH_DERIVED: types.append("derived") if flags & BOUNDARY_PATH_TEXTBOX: types.append("textbox") if flags & BOUNDARY_PATH_OUTERMOST: types.append("outermost") return types @dataclass(frozen=True) class BoundaryPathState: external: bool = False derived: bool = False textbox: bool = False outermost: bool = False @staticmethod def from_flags(flag: int) -> "BoundaryPathState": return BoundaryPathState( external=bool(flag & BOUNDARY_PATH_EXTERNAL), derived=bool(flag & BOUNDARY_PATH_DERIVED), textbox=bool(flag & BOUNDARY_PATH_TEXTBOX), outermost=bool(flag & BOUNDARY_PATH_OUTERMOST), ) @property def default(self) -> bool: return not ( self.external or self.derived or self.outermost or self.textbox ) GRADIENT_TYPES = frozenset( [ "LINEAR", "CYLINDER", "INVCYLINDER", "SPHERICAL", "INVSPHERICAL", "HEMISPHERICAL", "INVHEMISPHERICAL", "CURVED", "INVCURVED", ] ) # Viewport Status Flags (VSF) group code=90 VSF_PERSPECTIVE_MODE = 0x1 # enabled if set VSF_FRONT_CLIPPING = 0x2 # enabled if set VSF_BACK_CLIPPING = 0x4 # enabled if set VSF_USC_FOLLOW = 0x8 # enabled if set VSF_FRONT_CLIPPING_NOT_AT_EYE = 0x10 # enabled if set VSF_UCS_ICON_VISIBILITY = 0x20 # enabled if set VSF_UCS_ICON_AT_ORIGIN = 0x40 # enabled if set VSF_FAST_ZOOM = 0x80 # enabled if set VSF_SNAP_MODE = 0x100 # enabled if set VSF_GRID_MODE = 0x200 # enabled if set VSF_ISOMETRIC_SNAP_STYLE = 0x400 # enabled if set VSF_HIDE_PLOT_MODE = 0x800 # enabled if set # If set and kIsoPairRight is not set, then isopair top is enabled. # If both kIsoPairTop and kIsoPairRight are set, then isopair left is enabled: VSF_KISOPAIR_TOP = 0x1000 # If set and kIsoPairTop is not set, then isopair right is enabled: VSF_KISOPAIR_RIGHT = 0x2000 VSF_VIEWPORT_ZOOM_LOCKING = 0x4000 # enabled if set VSF_LOCK_ZOOM = 0x4000 # enabled if set VSF_CURRENTLY_ALWAYS_ENABLED = 0x8000 # always set without a meaning :) VSF_NON_RECTANGULAR_CLIPPING = 0x10000 # enabled if set VSF_TURN_VIEWPORT_OFF = 0x20000 VSF_NO_GRID_LIMITS = 0x40000 VSF_ADAPTIVE_GRID_DISPLAY = 0x80000 VSF_SUBDIVIDE_GRID = 0x100000 VSF_GRID_FOLLOW_WORKPLANE = 0x200000 # Viewport Render Mode (VRM) group code=281 VRM_2D_OPTIMIZED = 0 VRM_WIREFRAME = 1 VRM_HIDDEN_LINE = 2 VRM_FLAT_SHADED = 3 VRM_GOURAUD_SHADED = 4 VRM_FLAT_SHADED_WITH_WIREFRAME = 5 VRM_GOURAUD_SHADED_WITH_WIREFRAME = 6 IMAGE_SHOW = 1 IMAGE_SHOW_WHEN_NOT_ALIGNED = 2 IMAGE_USE_CLIPPING_BOUNDARY = 4 IMAGE_TRANSPARENCY_IS_ON = 8 UNDERLAY_CLIPPING = 1 UNDERLAY_ON = 2 UNDERLAY_MONOCHROME = 4 UNDERLAY_ADJUST_FOR_BG = 8 DIM_LINEAR = 0 DIM_ALIGNED = 1 DIM_ANGULAR = 2 DIM_DIAMETER = 3 DIM_RADIUS = 4 DIM_ANGULAR_3P = 5 DIM_ORDINATE = 6 DIM_ARC = 8 DIM_BLOCK_EXCLUSIVE = 32 DIM_ORDINATE_TYPE = 64 # unset for x-type, set for y-type DIM_USER_LOCATION_OVERRIDE = 128 DIMZIN_SUPPRESS_ZERO_FEET_AND_PRECISELY_ZERO_INCHES = 0 DIMZIN_INCLUDES_ZERO_FEET_AND_PRECISELY_ZERO_INCHES = 1 DIMZIN_INCLUDES_ZERO_FEET_AND_SUPPRESSES_ZERO_INCHES = 2 DIMZIN_INCLUDES_ZERO_INCHES_AND_SUPPRESSES_ZERO_FEET = 3 DIMZIN_SUPPRESSES_LEADING_ZEROS = 4 # only decimal dimensions DIMZIN_SUPPRESSES_TRAILING_ZEROS = 8 # only decimal dimensions # ATTRIB & ATTDEF flags ATTRIB_INVISIBLE = 1 # Attribute is invisible (does not appear) ATTRIB_CONST = 2 # This is a constant attribute ATTRIB_VERIFY = 4 # Verification is required on input of this attribute ATTRIB_IS_PRESET = 8 # no prompt during insertion # '|' is allowed in layer name, as ltype name ... INVALID_NAME_CHARACTERS = '<>/\\":;?*=`' INVALID_LAYER_NAME_CHARACTERS = set(INVALID_NAME_CHARACTERS) STD_SCALES = { 1: (1.0 / 128.0, 12.0), 2: (1.0 / 64.0, 12.0), 3: (1.0 / 32.0, 12.0), 4: (1.0 / 16.0, 12.0), 5: (3.0 / 32.0, 12.0), 6: (1.0 / 8.0, 12.0), 7: (3.0 / 16.0, 12.0), 8: (1.0 / 4.0, 12.0), 9: (3.0 / 8.0, 12.0), 10: (1.0 / 2.0, 12.0), 11: (3.0 / 4.0, 12.0), 12: (1.0, 12.0), 13: (3.0, 12.0), 14: (6.0, 12.0), 15: (12.0, 12.0), 16: (1.0, 1.0), 17: (1.0, 2.0), 18: (1.0, 4.0), 19: (1.0, 8.0), 20: (1.0, 10.0), 21: (1.0, 16.0), 22: (1.0, 20.0), 23: (1.0, 30.0), 24: (1.0, 40.0), 25: (1.0, 50.0), 26: (1.0, 100.0), 27: (2.0, 1.0), 28: (4.0, 1.0), 29: (8.0, 1.0), 30: (10.0, 1.0), 31: (100.0, 1.0), 32: (1000.0, 1.0), } RASTER_UNITS = { "none": 0, "mm": 1, "cm": 2, "m": 3, "km": 4, "in": 5, "ft": 6, "yd": 7, "mi": 8, } REVERSE_RASTER_UNITS = {value: name for name, value in RASTER_UNITS.items()} MODEL_SPACE_R2000 = "*Model_Space" MODEL_SPACE_R12 = "$Model_Space" PAPER_SPACE_R2000 = "*Paper_Space" PAPER_SPACE_R12 = "$Paper_Space" TMP_PAPER_SPACE_NAME = "*Paper_Space999999" MODEL_SPACE = { MODEL_SPACE_R2000.lower(), MODEL_SPACE_R12.lower(), } PAPER_SPACE = { PAPER_SPACE_R2000.lower(), PAPER_SPACE_R12.lower(), } LAYOUT_NAMES = { PAPER_SPACE_R2000.lower(), PAPER_SPACE_R12.lower(), MODEL_SPACE_R2000.lower(), MODEL_SPACE_R12.lower(), } # TODO: make enum DIMJUST = { "center": 0, "left": 1, "right": 2, "above1": 3, "above2": 4, } # TODO: make enum DIMTAD = { "above": 1, "center": 0, "below": 4, } DEFAULT_ENCODING = "cp1252" MLINE_TOP = 0 MLINE_ZERO = 1 MLINE_BOTTOM = 2 MLINE_HAS_VERTICES = 1 MLINE_CLOSED = 2 MLINE_SUPPRESS_START_CAPS = 4 MLINE_SUPPRESS_END_CAPS = 8 MLINESTYLE_FILL = 1 MLINESTYLE_MITER = 2 MLINESTYLE_START_SQUARE = 16 MLINESTYLE_START_INNER_ARC = 32 MLINESTYLE_START_ROUND = 64 MLINESTYLE_END_SQUARE = 256 MLINESTYLE_END_INNER_ARC = 512 MLINESTYLE_END_ROUND = 1024 # VP Layer Overrides OVR_ALPHA_KEY = "ADSK_XREC_LAYER_ALPHA_OVR" OVR_COLOR_KEY = "ADSK_XREC_LAYER_COLOR_OVR" OVR_LTYPE_KEY = "ADSK_XREC_LAYER_LINETYPE_OVR" OVR_LW_KEY = "ADSK_XREC_LAYER_LINEWT_OVR" OVR_ALPHA_CODE = 440 OVR_COLOR_CODE = 420 OVR_LTYPE_CODE = 343 OVR_LW_CODE = 91 OVR_VP_HANDLE_CODE = 335 OVR_APP_ALPHA = "{ADSK_LYR_ALPHA_OVERRIDE" OVR_APP_COLOR = "{ADSK_LYR_COLOR_OVERRIDE" OVR_APP_LTYPE = "{ADSK_LYR_LINETYPE_OVERRIDE" OVR_APP_LW = "{ADSK_LYR_LINEWT_OVERRIDE"