本章通过具体示例,演示如何使用 Python 编写 DXF 文件的读写程序。示例采用纯 Python 实现,不依赖第三方 CAD 库,便于理解 DXF 格式的核心原理。
无论使用何种编程语言,读取 DXF 文件的基本算法如下:
1. 打开 DXF 文件,逐行读取 2. 每两行为一个"组"(组码 + 值) 3. 根据组码 0 的值判断当前所在的段或图元类型 4. 在适当的段内处理对应的组码 5. 遇到 0/ENDSEC 表示当前段结束 6. 遇到 0/EOF 表示文件结束
以下是一个简单的 Python DXF 读取器示例,展示了如何解析 DXF 文件中的图元:
def read_dxf(filepath): """读取 DXF 文件,返回所有图元的列表""" with open(filepath, 'r', encoding='utf-8', errors='ignore') as f: lines = f.readlines() # 将 DXF 解析为组(group)列表 groups = [] i = 0 while i < len(lines) - 1: code = lines[i].strip() value = lines[i + 1].strip() if code: groups.append((int(code) if code.lstrip('-').isdigit() else code, value)) i += 2 # 按图元分组 entities = [] current_entity = None section = None for code, value in groups: if code == 0: if value == 'SECTION': if current_entity: entities.append(current_entity) current_entity = {'type': 'SECTION'} elif value == 'ENDSEC': if current_entity: entities.append(current_entity) current_entity = None elif value == 'EOF': break else: # 新的图元开始,保存上一个图元 if current_entity and current_entity.get('type') not in ('SECTION',): entities.append(current_entity) current_entity = {'type': value} else: if current_entity is not None: if code in (10, 11, 12, 13, 210): # 点的 X 坐标,等待 Y 和 Z current_entity.setdefault(code, {})['x'] = float(value) elif code in (20, 21, 22, 23, 220): pt_code = code - 10 # 对应的 X 组码 current_entity.setdefault(pt_code, {})['y'] = float(value) elif code in (30, 31, 32, 33, 230): pt_code = code - 20 # 对应的 X 组码 current_entity.setdefault(pt_code, {})['z'] = float(value) else: current_entity[code] = value return entities def print_entities(entities, entity_type=None): """打印图元信息""" for ent in entities: if entity_type and ent.get('type') != entity_type: continue print(f"\n--- {ent.get('type', 'UNKNOWN')} ---") if ent.get('type') == 'LINE': print(f" From: ({ent.get(10,{}).get('x',0)}, {ent.get(10,{}).get('y',0)})") print(f" To: ({ent.get(11,{}).get('x',0)}, {ent.get(11,{}).get('y',0)})") print(f" Layer: {ent.get(8, '0')}") elif ent.get('type') == 'CIRCLE': print(f" Center: ({ent.get(10,{}).get('x',0)}, {ent.get(10,{}).get('y',0)})") print(f" Radius: {ent.get(40, 'N/A')}") elif ent.get('type') == 'TEXT': print(f" Text: {ent.get(1, '')}") print(f" Position: ({ent.get(10,{}).get('x',0)}, {ent.get(10,{}).get('y',0)})") print(f" Height: {ent.get(40, 'N/A')}") elif ent.get('type') == 'LWPOLYLINE': print(f" Vertices count: {ent.get(90, 0)}") else: for k, v in ent.items(): if k != 'type': print(f" {k}: {v}") # 使用示例 if __name__ == '__main__': ents = read_dxf('sample.dxf') print_entities(ents, 'LINE') print_entities(ents, 'CIRCLE')
以下示例演示如何创建一个包含直线、圆和文字的 DXF 文件:
def write_dxf(filepath, entities): """将图元写入 DXF 文件""" with open(filepath, 'w', encoding='utf-8') as f: def w(code, value): f.write(f"{code}\n{value}\n") # HEADER 段 w(0, 'SECTION') w(2, 'HEADER') w(9, '$ACADVER') w(1, 'AC1027') w(0, 'ENDSEC') # CLASSES 段(空) w(0, 'SECTION') w(2, 'CLASSES') w(0, 'ENDSEC') # TABLES 段 w(0, 'SECTION') w(2, 'TABLES') # LAYER 表 w(0, 'TABLE') w(2, 'LAYER') w(5, 'F1') w(100, 'AcDbSymbolTable') w(70, '1') # 只包含 0 图层 w(0, 'LAYER') w(5, 'F2') w(100, 'AcDbLayerTableRecord') w(2, '0') w(70, '0') w(62, '7') # 白色 w(6, 'CONTINUOUS') w(370, '-3') # 默认线宽 w(0, 'ENDTAB') # LTYPE 表 w(0, 'TABLE') w(2, 'LTYPE') w(5, 'F3') w(100, 'AcDbSymbolTable') w(70, '1') w(0, 'LTYPE') w(5, 'F4') w(100, 'AcDbLinetypeTableRecord') w(2, 'CONTINUOUS') w(70, '0') w(3, 'Solid line') w(72, '65') w(73, '0') w(40, '0.0') w(0, 'ENDTAB') # BLOCK_RECORD 表 w(0, 'TABLE') w(2, 'BLOCK_RECORD') w(5, 'F5') w(100, 'AcDbSymbolTable') w(70, '1') w(0, 'BLOCK_RECORD') w(5, 'F6') w(100, 'AcDbBlockTableRecord') w(2, '*MODEL_SPACE') w(0, 'ENDTAB') w(0, 'ENDSEC') # BLOCKS 段 w(0, 'SECTION') w(2, 'BLOCKS') w(0, 'BLOCK') w(5, 'F7') w(100, 'AcDbBlockBegin') w(2, '*MODEL_SPACE') w(70, '0') w(10, '0.0') w(20, '0.0') w(30, '0.0') w(0, 'ENDBLK') w(5, 'F8') w(100, 'AcDbBlockEnd') w(0, 'ENDSEC') # ENTITIES 段 w(0, 'SECTION') w(2, 'ENTITIES') # 写入用户定义的图元 for ent in entities: for code, value in ent.items(): w(code, value) w(0, 'ENDSEC') w(0, 'EOF') def create_sample_dxf(): """创建一个示例 DXF 文件""" entities = [ # 直线 {'0': 'LINE', '5': 'F9', '100': 'AcDbLine', '8': '0', '10': '0.0', '20': '0.0', '30': '0.0', '11': '100.0', '21': '100.0', '31': '0.0'}, # 圆 {'0': 'CIRCLE', '5': 'FA', '100': 'AcDbCircle', '8': '0', '10': '50.0', '20': '50.0', '30': '0.0', '40': '25.0'}, # 文字 {'0': 'TEXT', '5': 'FB', '100': 'AcDbText', '8': '0', '10': '10.0', '20': '10.0', '30': '0.0', '1': 'Hello DXF', '40': '5.0', '7': 'STANDARD', '50': '0.0'}, ] write_dxf('output.dxf', entities) print("DXF 文件已创建: output.dxf") if __name__ == '__main__': create_sample_dxf()
以下是一个更实用的函数,用于提取常见图元的坐标信息:
def get_entity_coords(entity): """提取图元的主要坐标信息""" etype = entity.get('type', '') result = {'type': etype} if etype == 'LINE': result['start'] = (entity.get(10,{}).get('x',0), entity.get(10,{}).get('y',0)) result['end'] = (entity.get(11,{}).get('x',0), entity.get(11,{}).get('y',0)) elif etype == 'CIRCLE': result['center'] = (entity.get(10,{}).get('x',0), entity.get(10,{}).get('y',0)) result['radius'] = float(entity.get(40, 0)) elif etype == 'ARC': result['center'] = (entity.get(10,{}).get('x',0), entity.get(10,{}).get('y',0)) result['radius'] = float(entity.get(40, 0)) result['start_angle'] = float(entity.get(50, 0)) result['end_angle'] = float(entity.get(51, 0)) elif etype == 'TEXT': result['position'] = (entity.get(10,{}).get('x',0), entity.get(10,{}).get('y',0)) result['text'] = entity.get(1, '') result['height'] = float(entity.get(40, 0)) elif etype == 'LWPOLYLINE': vertices = [] for k, v in entity.items(): if isinstance(k, int) and k == 10: vertices.append(v) result['vertices'] = vertices result['closed'] = int(entity.get(70, 0)) & 1 elif etype == 'INSERT': result['block_name'] = entity.get(2, '') result['position'] = (entity.get(10,{}).get('x',0), entity.get(10,{}).get('y',0)) result['scale'] = (float(entity.get(41, 1)), float(entity.get(42, 1))) return result
对于 Python 开发,以下第三方库可简化 DXF 处理:
| 库名 | 说明 | 安装方式 |
| ezdxf | 功能最全面的 Python DXF 库 | pip install ezdxf |
| dxfwrite | 轻量级 DXF 写入库 | pip install dxfwrite |
使用 ezdxf 的示例:
import ezdxf # 读取 DXF doc = ezdxf.readfile('drawing.dxf') msp = doc.modelspace() # 遍历所有直线 for line in msp.query('LINE'): print(f"LINE: {line.dxf.start} -> {line.dxf.end}") # 创建 DXF doc = ezdxf.new('R2010') msp = doc.modelspace() msp.add_line((0, 0), (100, 100)) msp.add_circle((50, 50), 25) msp.add_text('Hello', height=5).set_pos((10, 10)) doc.saveas('output.dxf')
在手动创建 DXF 文件时,句柄必须唯一且格式正确:
本章通过 Python 代码示例,演示了 DXF 文件的基本读写方法和常见处理技巧。实际项目开发中,建议优先使用成熟的第三方库(如 ezdxf)以提高开发效率,但理解底层 DXF 格式对于调试和优化仍然十分重要。