import ezdxf import json import os import argparse # Mappings from ezdxf integer enums to human-readable strings MTEXT_ALIGNMENT_MAP = { 1: "TOP_LEFT", 2: "TOP_CENTER", 3: "TOP_RIGHT", 4: "MIDDLE_LEFT", 5: "MIDDLE_CENTER", 6: "MIDDLE_RIGHT", 7: "BOTTOM_LEFT", 8: "BOTTOM_CENTER", 9: "BOTTOM_RIGHT", } TEXT_HALIGN_MAP = {0: "LEFT", 1: "CENTER", 2: "RIGHT", 4: "MIDDLE"} TEXT_VALIGN_MAP = {0: "BASELINE", 1: "BOTTOM", 2: "MIDDLE", 3: "TOP"} def get_text_alignment(text_entity): """ Determines the alignment string from a TEXT or MTEXT entity. """ if text_entity.dxftype() == 'MTEXT': return MTEXT_ALIGNMENT_MAP.get(text_entity.dxf.attachment_point, "TOP_LEFT") elif text_entity.dxftype() == 'TEXT': # For TEXT, alignment is determined by halign and valign. # The default (0, 0) is LEFT, BASELINE which we treat as BOTTOM_LEFT. # If halign or valign is non-default, the align_point is used instead of insert. halign = TEXT_HALIGN_MAP.get(text_entity.dxf.halign, "LEFT") valign = TEXT_VALIGN_MAP.get(text_entity.dxf.valign, "BOTTOM") if valign == "BASELINE": # Treat BASELINE as BOTTOM for simplicity valign = "BOTTOM" # Combine them, e.g., "MIDDLE_CENTER" # For simple left-aligned text, it will be "BOTTOM_LEFT" if halign == "LEFT": return f"{valign}_LEFT" elif halign == "RIGHT": return f"{valign}_RIGHT" else: # CENTER or MIDDLE return f"{valign}_CENTER" return "BOTTOM_LEFT" # Default fallback def extract_dxf_entities(dxf_path): """ Extracts all LINE and TEXT/MTEXT entities from a DXF file and returns them as a dictionary structured for JSON output. Args: dxf_path (str): The full path to the DXF file. Returns: dict: A dictionary containing lists of extracted line and text entities, or None if the file cannot be processed. """ try: doc = ezdxf.readfile(dxf_path) msp = doc.modelspace() except (IOError, ezdxf.DXFStructureError) as e: print(f"Error reading DXF file at {dxf_path}: {e}") return None extracted_data = { "lines": [], "texts": [] } # --- Extract LINE entities --- for line in msp.query('LINE'): extracted_data["lines"].append({ "start": (line.dxf.start.x, line.dxf.start.y), "end": (line.dxf.end.x, line.dxf.end.y), "layer": line.dxf.layer, "color": line.dxf.color, "linetype": line.dxf.linetype }) # --- Extract TEXT and MTEXT entities --- for text_entity in msp.query('TEXT MTEXT'): alignment = get_text_alignment(text_entity) if text_entity.dxftype() == 'TEXT': text_content = text_entity.dxf.text height = text_entity.dxf.height # If text is aligned, its true position is align_point, not insert if text_entity.dxf.halign != 0 or text_entity.dxf.valign != 0: insert_point = (text_entity.dxf.align_point.x, text_entity.dxf.align_point.y) else: insert_point = (text_entity.dxf.insert.x, text_entity.dxf.insert.y) elif text_entity.dxftype() == 'MTEXT': text_content = text_entity.text # MTEXT uses a 'text' attribute height = text_entity.dxf.char_height insert_point = (text_entity.dxf.insert.x, text_entity.dxf.insert.y) else: continue extracted_data["texts"].append({ "content": text_content, "insert_point": insert_point, "alignment": alignment, "height": height, "style": text_entity.dxf.style, "layer": text_entity.dxf.layer, "color": text_entity.dxf.color }) return extracted_data def main(): parser = argparse.ArgumentParser(description="Extract entities (lines, texts, mtexts) from a DXF file and save them to a JSON file.") parser.add_argument("source_dxf", help="Path to the source DXF file.") parser.add_argument("output_json", help="Path to the output JSON file.") args = parser.parse_args() print(f"Extracting entities from: {args.source_dxf}") extracted_data = extract_dxf_entities(args.source_dxf) if extracted_data: try: with open(args.output_json, 'w', encoding='utf-8') as f: json.dump(extracted_data, f, ensure_ascii=False, indent=2) print(f"Successfully extracted {len(extracted_data.get('lines', []))} lines and " f"{len(extracted_data.get('texts', []))} text entities to: {args.output_json}") except IOError as e: print(f"Error: Could not write to output file {args.output_json}. Reason: {e}") if __name__ == "__main__": main()