import os import ezdxf from ezdxf.math import Matrix44, Vec3, BoundingBox, Vec2 import math import argparse import sys import shutil import configparser from utils import check_environment_var, setup_logger from pathlib import Path import logging # def get_bbox(entities, transform_matrix=None): # """ # Berechnet die Bounding Box für eine Liste von DXF-Entities. # Args: # entities: Liste von DXF-Entities (ezdxf entities) # Returns: # Vec2 or None: Zentrum der Bounding Box als Vec2-Objekt oder None, # falls keine gültige Geometrie gefunden wurde # Note: # Unterstützt POLYLINE, LWPOLYLINE und andere Entity-Typen. # Fehlerhafte Entities werden übersprungen und protokolliert. # """ # 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) # elif e.dxftype() == "ARC": # # Handle ARC entities: consider endpoints and cardinal extrema # try: # cx, cy = e.dxf.center.x, e.dxf.center.y # except Exception: # cx, cy = e.dxf.center # r = float(e.dxf.radius) # start_deg = float(e.dxf.start_angle) # end_deg = float(e.dxf.end_angle) # start = math.radians(start_deg % 360) # end = math.radians(end_deg % 360) # def in_sweep(a: float, s: float, e_: float) -> bool: # # CCW sweep from s to e_ with wrap handling # if e_ >= s: # return s <= a <= e_ # return a >= s or a <= e_ # candidates = [] # # Endpoints # candidates.append((cx + r * math.cos(start), cy + r * math.sin(start))) # candidates.append((cx + r * math.cos(end), cy + r * math.sin(end))) # # Cardinal angles 0, 90, 180, 270 deg # for ang in (0.0, math.pi/2, math.pi, 3*math.pi/2): # if in_sweep(ang, start, end): # candidates.append((cx + r * math.cos(ang), cy + r * math.sin(ang))) # for px, py in candidates: # min_x, min_y = min(min_x, px), min(min_y, py) # max_x, max_y = max(max_x, px), max(max_y, py) # elif e.dxftype() == "CIRCLE": # # Handle CIRCLE entities via center and radius # try: # cx, cy = e.dsf.center.x, e.dsf.center.y # except Exception: # cx, cy = e.dxf.center # r = float(e.dxf.radius) # min_x, min_y = min(min_x, cx - r), min(min_y, cy - r) # max_x, max_y = max(max_x, cx + r), max(max_y, cy + r) # elif e.dxftype() == "LINE" or e.dxftype()== "Line": # # Handle simple line entities by their start/end points # try: # sx, sy = e.dxf.start.x, e.dxf.start.y # ex, ey = e.dxf.end.x, e.dxf.end.y # except Exception: # # Some ezdxf versions provide tuples # (sx, sy), (ex, ey) = e.dxf.start, e.dxf.end # min_x, min_y = min(min_x, sx, ex ), min(min_y, sy, ey) # max_x, max_y = max(max_x, sx, ex), max(max_y, sy, ey) # elif e.dxftype() == "SPLINE": # # Approximate spline to compute bounding box # points = [] # try: # points = e.approximate(60) # except Exception: # try: # points = list(e.flattening(1.0)) # except Exception: # points = [] # if points: # for pt in points: # try: # px, py = pt.x, pt.y # except Exception: # px, py = pt[0], pt[1] # min_x, min_y = min(min_x, px), min(min_y, py) # max_x, max_y = max(max_x, px), max(max_y, py) # elif e.dxftype() == 'INSERT': # # INSERT: Block-Inhalt mit Transformation berücksichtigen # insert_bbox = calculate_insert_bounding_box(e, doc, transform_matrix) # if insert_bbox and insert_bbox.has_data: # bbox.extend(insert_bbox) # else: # 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, (0,0) # return Vec2((min_x + max_x) / 2, (min_y + max_y) / 2), (max_x -min_x, max_y -min_y) def create_block_library(input_dir, output_file, config, logger=None): """ Erstellt eine DXF-Block-Bibliothek aus einzelnen DXF-Dateien. Diese Funktion liest alle DXF-Dateien aus einem Verzeichnis und erstellt daraus eine Bibliothek mit Blöcken. Die Blöcke werden in einem Raster angeordnet und mit Beschriftungen versehen. Fehlerhafte Dateien werden protokolliert. Args: input_dir (str): Verzeichnis mit den zu verarbeitenden DXF-Dateien output_file (str): Pfad zur zu erstellenden Bibliotheks-DXF-Datei config: ConfigParser-Objekt mit den Konfigurationswerten logger: Optionaler Logger für Logging-Ausgaben Note: - Unterstützte Entity-Typen: LINE, LWPOLYLINE, POLYLINE, SPLINE, CIRCLE, ARC, INSERT - Blöcke werden zentriert und in einem 20x20 Raster angeordnet - Erstellt eine Log-Datei mit Zeitstempel für fehlerhafte Dateien - Automatische Erstellung des Output-Verzeichnisses falls nötig """ doc = ezdxf.new() msp = doc.modelspace() x_offset = 0 y_offset = 0 blocks_in_row = 0 error_files = [] processed_files = [] max_blockspacing_x = 0 max_blockspacing_y = 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", "INSERT"} filtered_entities = [] for e in entities: if e.dxftype() in allowed_types: filtered_entities.append(e) else: logger.info(f"{e.dxftype()} is nicht in der erlaubten Liste. Diese befindet sich in {filename}") entities = filtered_entities except Exception as e: error_msg = f"Fehler beim Lesen von {filename}: {e}" if logger: logger.error(error_msg) else: print(error_msg) error_files.append((filename, error_msg)) continue # Sicherstellen, dass die im Quell-DXF verwendeten Layer im Zieldokument existieren try: used_layer_names = {e.dxf.layer for e in entities if hasattr(e.dxf, "layer")} for layer_name in used_layer_names: if layer_name and layer_name not in doc.layers: try: src_layer = src_doc.layers.get(layer_name) doc.layers.add( name=layer_name, color=getattr(src_layer.dxf, "color", None), linetype=getattr(src_layer.dxf, "linetype", None), lineweight=getattr(src_layer.dxf, "lineweight", None), ) except Exception: # Fallback: Layer mit Standardwerten anlegen doc.layers.add(name=layer_name) except Exception: pass # center, ausdehnung = get_bbox(entities) center, ausdehnung = get_entity_bounding_box(entities, doc, transform_matrix=None) if center is None: error_msg = f"Keine gültige Geometrie in {filename}" if logger: logger.error(error_msg) else: print(error_msg) error_files.append((filename, error_msg)) 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: # # Sicherstellen, dass referenzierte Blöcke für INSERT verfügbar sind # if e.dxftype() == "INSERT": # handle_insert_entities(doc, src_doc, e) cp = copy_entity(logger, error_files, filename, e, center) if cp: blk.add_entity(cp) # Platzierung in Reihen und Spalten # Attribut-Definition (ATTDEF) hinzufügen blk.add_attdef( tag="NAME", insert=(0.2, 0.2), # Position relativ zum Blockursprung ) # Blockreferenz-Layer bestimmen blockref_layer = None try: #Konfiguration erlaubt expliziten Layernamen cfg_layer = None try: cfg_layer = config.get("dxf2lib", "blockref_layer") except Exception: cfg_layer = None if cfg_layer and cfg_layer.strip(): blockref_layer = cfg_layer.strip() else: # Dominanten Ursprungs-Layer verwenden (Mehrheit der Entities) layer_counts = {} for e in entities: ln = getattr(e.dxf, "layer", None) if not ln: continue layer_counts[ln] = layer_counts.get(ln, 0) + 1 if layer_counts: blockref_layer = max(layer_counts.items(), key=lambda kv: kv[1])[0] if blockref_layer and blockref_layer not in doc.layers: # Sicherstellen, dass der Layer existiert try: src_layer = src_doc.layers.get(blockref_layer) doc.layers.add( name=blockref_layer, color=getattr(src_layer.dxf, "color", None), linetype=getattr(src_layer.dxf, "linetype", None), lineweight=getattr(src_layer.dxf, "lineweight", None), ) except Exception: doc.layers.add(name=blockref_layer) except Exception: blockref_layer = None section = "dxf2lib" text_height = get_cfg_value(section, "text_height", DEFAULTS["text_height"]) extra_block_space_x = get_cfg_value(section, "extra_block_space_x", DEFAULTS["extra_block_space_x"]) blocks_per_row = get_cfg_value(section, "blocks_per_row", DEFAULTS["blocks_per_row"]) extra_text_space_y = get_cfg_value(section, "extra_text_space_y", DEFAULTS["extra_text_space_y"]) ausdehnung_x, ausdehung_y = ausdehnung[0], ausdehnung[1] block_spacing_y = ausdehung_y + 400 block_spacing_x = ausdehnung_x + extra_block_space_x max_blockspacing_x = max(max_blockspacing_x, block_spacing_x) max_blockspacing_y = max(max_blockspacing_y, block_spacing_y) x_offset += max_blockspacing_x # Blockreferenz mit optionalem Layer einfügen (Entity-Layer bleiben erhalten) if blockref_layer: msp.add_blockref(name, insert=(x_offset, y_offset), dxfattribs={"layer": blockref_layer}) else: msp.add_blockref(name, insert=(x_offset, y_offset)) # Text mit Blocknamen über dem Block # Werte aus Config holen (Block: [dxf2lib]) msp.add_text(name, dxfattribs={'height': text_height, 'insert': (x_offset -ausdehnung_x/2 , y_offset + ausdehung_y/2 + extra_text_space_y)}) processed_files.append(filename) blocks_in_row += 1 # Abstand zwischen Blöcken in einer Reihe if blocks_in_row == blocks_per_row: blocks_in_row = 0 x_offset = 0 y_offset -= max_blockspacing_y # Neue Zeile, nach unten versetzt max_blockspacing_y = 0 max_blockspacing_x = 0 if logger: logger.info(f"Bibliotheks-DXF gespeichert: {output_file}") logger.info(f"Verarbeitete Dateien: {len(processed_files)}") logger.info(f"Fehlerhafte Dateien: {len(error_files)}") if error_files: logger.warning(f"{len(error_files)} Dateien konnten nicht verarbeitet werden:") for filename, error_msg in error_files: logger.error(f"{filename}: {error_msg}") else: logger.info("Keine fehlerhaften Dateien gefunden.") else: print(f"Bibliotheks-DXF gespeichert: {output_file}") print(f"Verarbeitete Dateien: {len(processed_files)}") print(f"Fehlerhafte Dateien: {len(error_files)}") if error_files: print(f"Warnung: {len(error_files)} Dateien konnten nicht verarbeitet werden.") output_dir = output_file.parent if not output_dir.exists(): output_dir.mkdir(parents=True, exist_ok=True) doc.saveas(output_file) def copy_entity(logger, error_files, filename, e, center): try: cp = e.copy() # Geometrie verschieben! cp.translate(-center.x, -center.y, 0) # Ursprungs-Layer übernehmen if hasattr(e.dxf, "layer"): cp.dxf.layer = e.dxf.layer if (hasattr(e.dxf, "color")): cp.dxf.color = e.dxf.color return cp except Exception as err: error_msg = f"Fehler beim Verarbeiten von Entity {e.dxftype()} in {filename}: {err}" if logger: logger.error(error_msg) else: print(error_msg) error_files.append((filename, error_msg)) return None def handle_insert_entities(doc, src_doc, e): try: ref_name = e.dxf.name if ref_name not in doc.blocks and ref_name in src_doc.blocks: src_blk = src_doc.blocks[ref_name] dst_blk = doc.blocks.new(name=ref_name) # Basis­punkt übernehmen, falls vorhanden try: dst_blk.block.dxf.base_point = src_blk.block.dxf.base_point except Exception: pass for ent in src_blk: dst_blk.add_entity(ent.copy()) except Exception: pass # Standardwerte (falls nicht in der Config) DEFAULTS = { "text_height": 20, # Schriftgröße des Texts (in DXF-Einheiten) "blocks_per_row": 20, # Anzahl Blöcke pro Zeile im Raster "extra_block_space_x" : 50, # Extra Platz damit sich Blöcke nicht überlappen "extra_text_space_y" : 50 # Abstand der Überschrift über dem Symbol } def get_cfg_value(section, key, fallback): try: return int(config.get(section, key)) except Exception: return fallback def convert_dxf_to_block_with_inserts(input_filename, output_filename, block_name="CONVERTED_BLOCK"): """ Konvertiert alle Entities einer DXF-Datei in einen neuen Block INSERTs werden als Referenzen beibehalten (nicht explodiert) """ try: # Eingabe-DXF laden input_doc = ezdxf.readfile(input_filename) print(f"Lade DXF-Datei: {input_filename}") # Neue Ausgabe-DXF erstellen output_doc = ezdxf.new('R2010') output_doc.header['$INSUNITS'] = 4 # Millimeter # Zuerst alle Block-Definitionen kopieren copied_blocks = copy_block_definitions(input_doc, output_doc) print(f"Block-Definitionen kopiert: {len(copied_blocks)}") # Neuen Hauptblock erstellen new_block = output_doc.blocks.new(name=block_name) print(f"Erstelle neuen Block: {block_name}") # Alle Entities aus dem Modelspace kopieren msp = input_doc.modelspace() entity_count = 0 insert_count = 0 for entity in msp: if entity.dxftype() == 'INSERT': # INSERT direkt kopieren (nicht explodieren) copy_entity_to_block(entity, new_block) insert_count += 1 else: # Normale Entity kopieren copy_entity_to_block(entity, new_block) entity_count += 1 # Bounding Box berechnen bbox = calculate_block_bounding_box(new_block, output_doc) # Block im Modelspace der neuen Datei platzieren output_msp = output_doc.modelspace() output_msp.add_blockref(block_name, insert=(0, 0)) # Bounding Box als Hilfslinien hinzufügen (optional) if bbox.has_data: add_bounding_box_to_modelspace(output_msp, bbox) # Speichern output_doc.saveas(output_filename) print(f"Konvertierung abgeschlossen:") print(f" - {entity_count} Entities übertragen") print(f" - {insert_count} INSERTs als Referenzen beibehalten") print(f" - Bounding Box: {format_bounding_box(bbox)}") print(f" - Ausgabe: {output_filename}") return bbox except FileNotFoundError: print(f"Fehler: Datei {input_filename} nicht gefunden") return None except ezdxf.DXFStructureError as e: print(f"DXF-Strukturfehler: {e}") return None except Exception as e: print(f"Unerwarteter Fehler: {e}") return None def copy_block_definitions(source_doc, target_doc): """ Kopiert alle Block-Definitionen vom Quell- zum Ziel-Dokument """ copied_blocks = [] for block_name in source_doc.blocks: # Standard-Blöcke (MODEL_SPACE, PAPER_SPACE) überspringen if block_name.startswith('*'): continue source_block = source_doc.blocks[block_name] # Prüfen ob Block bereits existiert if block_name in target_doc.blocks: print(f"Warnung: Block '{block_name}' existiert bereits, wird übersprungen") continue # Neuen Block in Ziel-Dokument erstellen target_block = target_doc.blocks.new(name=block_name) # Alle Entities des Quell-Blocks kopieren for entity in source_block: copy_entity_to_block(entity, target_block) copied_blocks.append(block_name) return copied_blocks def calculate_block_bounding_box(block, doc): """ Berechnet die Bounding Box eines Blocks inklusive aller INSERTs """ bbox = BoundingBox() for entity in block: entity_bbox = get_entity_bounding_box(entity, doc) if entity_bbox and entity_bbox.has_data: bbox.extend(entity_bbox) return bbox def get_entity_bounding_box(entity, doc, transform_matrix=None): """ Berechnet die Bounding Box einer einzelnen Entity Berücksichtigt INSERTs mit ihren Block-Inhalten """ bbox = BoundingBox() for e in entity: try: if e.dxftype() == 'LINE': start = Vec3(e.dxf.start) end = Vec3(e.dxf.end) if transform_matrix: start = transform_matrix.transform(start) end = transform_matrix.transform(end) bbox.extend([start, end]) elif e.dxftype() == 'CIRCLE': center = Vec3(e.dxf.center) radius = e.dxf.radius if transform_matrix: center = transform_matrix.transform(center) # Radius mit durchschnittlicher Skalierung anpassen scale_factor = (transform_matrix.scale_x + transform_matrix.scale_y) / 2 radius *= abs(scale_factor) bbox.extend([ Vec3(center.x - radius, center.y - radius, center.z), Vec3(center.x + radius, center.y + radius, center.z) ]) elif e.dxftype() == 'ARC': # Vereinfachung: Bounding Box des vollständigen Kreises center = Vec3(e.dxf.center) radius = e.dxf.radius if transform_matrix: center = transform_matrix.transform(center) scale_factor = (transform_matrix.scale_x + transform_matrix.scale_y) / 2 radius *= abs(scale_factor) bbox.extend([ Vec3(center.x - radius, center.y - radius, center.z), Vec3(center.x + radius, center.y + radius, center.z) ]) elif e.dxftype() == 'LWPOLYLINE': points = [] for point in e.get_points(): pt = Vec3(point[0], point[1], 0) if transform_matrix: pt = transform_matrix.transform(pt) points.append(pt) if points: bbox.extend(points) elif e.dxftype() == 'POLYLINE': points = [] for vertex in e.vertices: pt = Vec3(vertex.dxf.location) if transform_matrix: pt = transform_matrix.transform(pt) points.append(pt) if points: bbox.extend(points) elif e.dxftype() == 'TEXT': # Vereinfachung: Nur Insert-Point berücksichtigen insert_point = Vec3(e.dxf.insert) if transform_matrix: insert_point = transform_matrix.transform(insert_point) bbox.extend([insert_point]) elif e.dxftype() == 'INSERT': # INSERT: Block-Inhalt mit Transformation berücksichtigen insert_bbox = calculate_insert_bounding_box(e, doc, transform_matrix) if insert_bbox and insert_bbox.has_data: bbox.extend(insert_bbox) except Exception as e: print(f"Fehler bei Bounding Box Berechnung für {entity.dxftype()}: {e}") return bbox,( bbox.extmax.x - bbox.extmin.x,bbox.extmax.y -bbox.extmin.y) def calculate_insert_bounding_box(insert_entity, doc, parent_transform=None): """ Berechnet die Bounding Box eines INSERTs inklusive Block-Inhalt """ try: # Block-Definition finden block_name = insert_entity.dxf.name if block_name not in doc.blocks: print(f"Warnung: Block '{block_name}' nicht gefunden") return BoundingBox() block_def = doc.blocks[block_name] # Transformation der INSERT-Entity berechnen insert_transform = get_insert_transform_matrix(insert_entity) # Mit übergeordneter Transformation kombinieren if parent_transform: combined_transform = parent_transform * insert_transform else: combined_transform = insert_transform # Bounding Box aller Entities im Block berechnen block_bbox = BoundingBox() for block_entity in block_def: entity_bbox = get_entity_bounding_box(block_entity, doc, combined_transform) if entity_bbox and entity_bbox.has_data: block_bbox.extend(entity_bbox) return block_bbox except Exception as e: print(f"Fehler bei INSERT Bounding Box: {e}") return BoundingBox() def get_insert_transform_matrix(insert_entity): """ Berechnet die Transformationsmatrix für einen INSERT """ # Position insert_point = Vec3(insert_entity.dxf.insert) # Skalierung xscale = getattr(insert_entity.dxf, 'xscale', 1.0) yscale = getattr(insert_entity.dxf, 'yscale', 1.0) zscale = getattr(insert_entity.dxf, 'zscale', 1.0) # Rotation (in Radiant umwandeln) rotation = math.radians(getattr(insert_entity.dxf, 'rotation', 0.0)) # Transformationsmatrix erstellen matrix = Matrix44.chain( Matrix44.scale(xscale, yscale, zscale), Matrix44.z_rotate(rotation), Matrix44.translate(insert_point.x, insert_point.y, insert_point.z) ) return matrix def copy_entity_to_block(entity, target_block): """ Kopiert eine Entity in einen Zielblock """ try: # Entity kopieren und zum Block hinzufügen entity_copy = entity.copy() target_block.add_entity(entity_copy) except Exception as e: print(f"Fehler beim Kopieren von {entity.dxftype()}: {e}") def add_bounding_box_to_modelspace(msp, bbox): """ Fügt die Bounding Box als Hilfslinien zum Modelspace hinzu """ if not bbox.has_data: return # Bounding Box Rechteck zeichnen min_pt = bbox.extmin max_pt = bbox.extmax # Rechteck als LWPOLYLINE bbox_points = [ (min_pt.x, min_pt.y), (max_pt.x, min_pt.y), (max_pt.x, max_pt.y), (min_pt.x, max_pt.y), (min_pt.x, min_pt.y) ] bbox_poly = msp.add_lwpolyline(bbox_points) bbox_poly.dxf.layer = "BOUNDING_BOX" bbox_poly.dxf.color = 1 # Rot # Bemaßungen hinzufügen width = max_pt.x - min_pt.x height = max_pt.y - min_pt.y # Text mit Abmessungen text_pos = Vec3(min_pt.x, max_pt.y + 5, 0) msp.add_text(f"Breite: {width:.2f} mm", height=3, dxfattribs={'insert': text_pos, 'layer': "BOUNDING_BOX", 'color': 1}) text_pos2 = Vec3(min_pt.x, max_pt.y + 10, 0) msp.add_text(f"Höhe: {height:.2f} mm", height=3, dxfattribs={'insert': text_pos2, 'layer': "BOUNDING_BOX", 'color': 1}) def format_bounding_box(bbox): """ Formatiert Bounding Box Information für Ausgabe """ if not bbox.has_data: return "Keine gültigen Geometriedaten gefunden" min_pt = bbox.extmin max_pt = bbox.extmax width = max_pt.x - min_pt.x height = max_pt.y - min_pt.y depth = max_pt.z - min_pt.z return (f"Min: ({min_pt.x:.2f}, {min_pt.y:.2f}, {min_pt.z:.2f}) " f"Max: ({max_pt.x:.2f}, {max_pt.y:.2f}, {max_pt.z:.2f}) " f"Größe: {width:.2f} × {height:.2f} × {depth:.2f} mm") def analyze_source_dxf_with_blocks(filename): """ Analysiert die Quell-DXF inklusive Block-Definitionen """ try: doc = ezdxf.readfile(filename) msp = doc.modelspace() entity_types = {} layer_count = {} insert_blocks = {} block_definitions = {} # Modelspace analysieren for entity in msp: entity_type = entity.dxftype() entity_types[entity_type] = entity_types.get(entity_type, 0) + 1 layer = getattr(entity.dxf, 'layer', '0') layer_count[layer] = layer_count.get(layer, 0) + 1 if entity_type == 'INSERT': block_name = entity.dxf.name insert_blocks[block_name] = insert_blocks.get(block_name, 0) + 1 # Block-Definitionen analysieren for block_name in doc.blocks: if not block_name.startswith('*'): # Keine Standard-Blöcke block_def = doc.blocks[block_name] entity_count = len(list(block_def)) block_definitions[block_name] = entity_count print(f"\nAnalyse von {filename}:") print("=" * 50) print("Entity-Typen im Modelspace:") for etype, count in sorted(entity_types.items()): print(f" {etype}: {count}") print(f"\nLayer ({len(layer_count)}):") for layer, count in sorted(layer_count.items()): print(f" {layer}: {count} entities") if insert_blocks: print(f"\nINSERT-Verwendungen ({sum(insert_blocks.values())} total):") for block, count in sorted(insert_blocks.items()): print(f" {block}: {count}× verwendet") if block_definitions: print(f"\nBlock-Definitionen ({len(block_definitions)}):") for block, count in sorted(block_definitions.items()): print(f" {block}: {count} entities") return entity_types, layer_count, insert_blocks, block_definitions except Exception as e: print(f"Fehler bei der Analyse: {e}") return {}, {}, {}, {} 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)', default="test") 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 = Path(args.input) print(f"Verwende Input-Verzeichnis: {INPUT_DIR} \n") else: INPUT_DIR = check_environment_var("PROJECT_DATA") / "dxf" print(f"Kein Input-Verzeichnis angegeben, verwende Standard: {INPUT_DIR} \n") OUTPUT_FILE = check_environment_var("PROJECT_DATA") / "block_libraries" / f"{args.name}.dxf" # Prüfe und erstelle log-Verzeichnis falls nötig log_dir = check_environment_var("PROJECT_LOG") if not log_dir.exists(): log_dir.mkdir(parents=True, exist_ok=True) print(f"Log-Verzeichnis erstellt: {log_dir}") # Logger Setup log_file = Path(os.environ['PROJECT_LOG']) / 'dxf2lib.log' file_handler = logging.FileHandler(str(log_file), 'a', 'utf-8') logger = setup_logger(log_dir, name='dxf2lib') logger.info("=== DXF2LIB Verarbeitung gestartet ===") logger.info(f"Input-Verzeichnis: {INPUT_DIR}") logger.info(f"Output-Datei: {OUTPUT_FILE}") logger.addHandler(file_handler) # Lade Config-Datei config = configparser.ConfigParser() config_path = check_environment_var("PROJECT_CFG") / "allgemein.cfg" config.read(config_path, encoding="utf-8") logger.info(f"Config-Datei geladen: {config_path}") # Erstelle die Block-Bibliothek logger.info(f"Erstelle Block-Bibliothek aus {INPUT_DIR}...") create_block_library(INPUT_DIR, OUTPUT_FILE, config, logger) logger.info("=== DXF2LIB Verarbeitung abgeschlossen ===")