import os import ezdxf from ezdxf.math import Vec2 import argparse import sys import shutil def check_environment_var(env_str: str, fallback: str) -> str: out_path = os.environ.get(env_str) if out_path: return out_path print(f"[WARN] Umgebungsvariable {env_str} ist nicht gesetzt oder leer. Verwende '{fallback}'.") return fallback def get_bbox(entities): min_x, min_y = float('inf'), float('inf') max_x, max_y = float('-inf'), float('-inf') for e in entities: try: if e.dxftype() == "POLYLINE": for vertex in e.vertices: if hasattr(vertex.dxf, 'location'): x, y = vertex.dxf.location.x, vertex.dxf.location.y min_x, min_y = min(min_x, x), min(min_y, y) max_x, max_y = max(max_x, x), max(max_y, y) elif e.dxftype() == "LWPOLYLINE": for x, y, *_ in e.get_points("xy"): min_x, min_y = min(min_x, x), min(min_y, y) max_x, max_y = max(max_x, x), max(max_y, y) else: # Für andere Entity-Typen: Standard bbox() verwenden box = e.bbox() if box: (x1, y1), (x2, y2) = box.extmin, box.extmax min_x, min_y = min(min_x, x1), min(min_y, y1) max_x, max_y = max(max_x, x2), max(max_y, y2) except Exception as err: print(f" BBox Fehler für {e.dxftype()}: {err}") continue if min_x == float('inf'): return None return Vec2((min_x + max_x) / 2, (min_y + max_y) / 2) if __name__ == "__main__": # Argumentparser für Kommandozeilenoptionen parser = argparse.ArgumentParser(description="SVG/XML zu DXF Konverter") parser.add_argument('-i', '--input', type=str, help='Input-Verzeichnis mit SVG/XML-Dateien') parser.add_argument('-n', '--name', required=False, type=str, help='Name der zu erzeugenden Bibliothek (optional, wird sonst abgefragt)') parser.add_argument('-k', '--keep', action="store_true", help='Behalte converted_dxf Ordner. Sonst Löschen nach erfolgreicher Bibliothekserstellung') if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"): parser.print_help() sys.exit(0) args = parser.parse_args() if not args.name: args.name = input("Bitte Namen der zu erzeugenden Bibliothek eingeben: ").strip() if not args.name: print("Fehler: Kein Name angegeben. Beende.") sys.exit(1) # Verzeichnisse über Umgebungsvariablen oder Fallback if args.input: INPUT_DIR = args.input print(f"Verwende Input-Verzeichnis: {INPUT_DIR} \n") else: INPUT_DIR = os.path.join(check_environment_var("PROJECT_WORK", "."), "converted_dxfs") print(f"Kein Input-Verzeichnis angegeben, verwende Standard: {INPUT_DIR} \n") OUTPUT_FILE = os.path.join(check_environment_var("PROJECT_DATA", "."),"block_libaries", f"{args.name}.dxf") doc = ezdxf.new() msp = doc.modelspace() x_offset = 0 y_offset = 0 blocks_in_row = 0 for filename in os.listdir(INPUT_DIR): if not filename.lower().endswith(".dxf"): continue filepath = os.path.join(INPUT_DIR, filename) name = os.path.splitext(filename)[0] try: src_doc = ezdxf.readfile(filepath) src_msp = src_doc.modelspace() entities = list(src_msp) allowed_types = {"LINE", "LWPOLYLINE", "POLYLINE", "SPLINE", "CIRCLE", "ARC"} filtered_entities = [e for e in entities if e.dxftype() in allowed_types] entities = filtered_entities except Exception as e: print(f"Fehler beim Lesen von {filename}: {e}") continue center = get_bbox(entities) if center is None: print(f"Keine gültige Geometrie in {filename}") continue if name in doc.blocks: doc.blocks.delete_block(name) blk = doc.blocks.new(name=name, base_point=(0,0)) for e in entities: try: cp = e.copy() cp.translate(-center.x, -center.y, 0) # Geometrie verschieben! blk.add_entity(cp) except Exception as err: print(f"Fehler in {filename}: {err}") # Platzierung in Reihen und Spalten msp.add_blockref(name, insert=(x_offset, y_offset)) # Text mit Blocknamen über dem Block text_y = y_offset + 175 # 175 Einheiten über dem Block max_len = 9 text_height = 20 line_spacing = 22 # Abstand zwischen den zwei Textzeilen if len(name) > max_len: first_line = name[:max_len] second_line = name[max_len:] # obere Zeile msp.add_text(first_line, dxfattribs={'height': text_height, 'insert': (x_offset - 150, text_y)}) # untere Zeile, etwas niedriger (y kleiner) msp.add_text(second_line, dxfattribs={'height': text_height, 'insert': (x_offset - 150, text_y - line_spacing)}) else: msp.add_text(name, dxfattribs={'height': text_height, 'insert': (x_offset - 150, text_y)}) blocks_in_row += 1 x_offset += 350 # Abstand zwischen Blöcken in einer Reihe if blocks_in_row == 20: blocks_in_row = 0 x_offset = 0 y_offset -= 500 # Neue Zeile, nach unten versetzt print(f"Bibliotheks-DXF gespeichert: {OUTPUT_FILE} \n") output_dir = os.path.dirname(OUTPUT_FILE) if not os.path.exists(output_dir): os.makedirs(output_dir, exist_ok=True) doc.saveas(OUTPUT_FILE) if not args.keep: print(f"Keep-Argument nicht gesetzt, lösche Input-Verzeichnis: {INPUT_DIR} \n") try: if os.path.exists(INPUT_DIR): shutil.rmtree(INPUT_DIR) print(f"Verzeichnis '{INPUT_DIR}' wurde gelöscht.") except Exception as err: print(f"[WARN] Konnte '{INPUT_DIR}' nicht löschen: {err}")