logging in plant2dxf.py, svg2dxf.py und dxf2lib.py implementiert
SVG2DXF: Inkscape-Option hinzugefügt, die mit einem SVG Parser arbeitet
This commit is contained in:
+4
-74
@@ -4,31 +4,10 @@ from ezdxf.math import Vec2
|
||||
import argparse
|
||||
import sys
|
||||
import shutil
|
||||
import datetime
|
||||
import configparser
|
||||
import logging
|
||||
from utils import check_environment_var, setup_logger
|
||||
|
||||
|
||||
def check_environment_var(env_str: str, fallback: str) -> str:
|
||||
"""
|
||||
Prüft eine Umgebungsvariable und gibt den Wert oder einen Fallback zurück.
|
||||
|
||||
Args:
|
||||
env_str (str): Name der zu prüfenden Umgebungsvariable
|
||||
fallback (str): Fallback-Wert, falls die Variable nicht gesetzt ist
|
||||
|
||||
Returns:
|
||||
str: Wert der Umgebungsvariable oder Fallback-Wert
|
||||
|
||||
Note:
|
||||
Gibt eine Warnung aus, falls die Umgebungsvariable nicht gesetzt ist
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Berechnet die Bounding Box für eine Liste von DXF-Entities.
|
||||
@@ -75,51 +54,6 @@ def get_bbox(entities):
|
||||
return Vec2((min_x + max_x) / 2, (min_y + max_y) / 2)
|
||||
|
||||
|
||||
def setup_logger(log_dir):
|
||||
"""
|
||||
Erstellt und konfiguriert einen Logger für die DXF2LIB Verarbeitung.
|
||||
|
||||
Args:
|
||||
log_dir (str): Verzeichnis für die Log-Dateien
|
||||
|
||||
Returns:
|
||||
logging.Logger: Konfigurierter Logger
|
||||
|
||||
Note:
|
||||
Erstellt eine Log-Datei mit Zeitstempel und formatiert die Ausgaben
|
||||
"""
|
||||
# Logger erstellen
|
||||
logger = logging.getLogger('dxf2lib')
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# Bestehende Handler entfernen (falls Logger bereits existiert)
|
||||
for handler in logger.handlers[:]:
|
||||
logger.removeHandler(handler)
|
||||
|
||||
# Zeitstempel für Log-Datei
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
log_file = os.path.join(log_dir, f"dxf2lib_{timestamp}.log")
|
||||
|
||||
# File Handler erstellen
|
||||
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||
file_handler.setLevel(logging.INFO)
|
||||
|
||||
# Console Handler erstellen
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
|
||||
# Formatter erstellen
|
||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
# Handler zum Logger hinzufügen
|
||||
logger.addHandler(file_handler)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
return logger, log_file
|
||||
|
||||
|
||||
def create_block_library(input_dir, output_file, config, logger=None):
|
||||
"""
|
||||
Erstellt eine DXF-Block-Bibliothek aus einzelnen DXF-Dateien.
|
||||
@@ -299,12 +233,8 @@ if __name__ == "__main__":
|
||||
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")
|
||||
INPUT_DIR = check_environment_var("PROJECT_WORK", ".")
|
||||
print(f"Verwende Input-Verzeichnis: {INPUT_DIR} \n")
|
||||
|
||||
OUTPUT_FILE = os.path.join(check_environment_var("PROJECT_DATA", "."),"block_libaries", f"{args.name}.dxf")
|
||||
|
||||
@@ -315,7 +245,7 @@ if __name__ == "__main__":
|
||||
print(f"Log-Verzeichnis erstellt: {log_dir}")
|
||||
|
||||
# Logger Setup
|
||||
logger, log_file = setup_logger(log_dir)
|
||||
logger, log_file = 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}")
|
||||
|
||||
+77
-30
@@ -13,12 +13,21 @@ import configparser
|
||||
import ezdxf
|
||||
from pathlib import Path
|
||||
import math
|
||||
from utils import check_environment_var, setup_logger
|
||||
|
||||
# --------------------------------------------------------- CFG-Leser für shapes.cfg
|
||||
def get_shape_cfg(teileart, cfg_path):
|
||||
def get_shape_cfg(teileart, cfg_path, logger=None):
|
||||
parser = configparser.ConfigParser()
|
||||
with open(cfg_path, encoding='utf-8') as f:
|
||||
parser.read_file(f)
|
||||
try:
|
||||
with open(cfg_path, encoding='utf-8') as f:
|
||||
parser.read_file(f)
|
||||
except Exception as e:
|
||||
msg = f"Fehler beim Lesen der Config-Datei {cfg_path}: {e}"
|
||||
if logger:
|
||||
logger.error(msg)
|
||||
else:
|
||||
print(msg)
|
||||
return []
|
||||
section = teileart
|
||||
if section not in parser:
|
||||
return []
|
||||
@@ -53,14 +62,6 @@ ATTR_TAG = "TeileId" # Attributtag im Block
|
||||
RADIUS = 400 # Radius der Kreiselkreise (mm)
|
||||
|
||||
# --------------------------------------------------------- Hilfsfunktionen
|
||||
def check_environment_var(env_str: str) -> Path:
|
||||
"""Liefert Path aus Umgebungsvariable oder beendet mit Fehlermeldung."""
|
||||
out_path = os.environ.get(env_str)
|
||||
if out_path:
|
||||
return Path(out_path)
|
||||
print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
|
||||
sys.exit(1)
|
||||
|
||||
def extract_coords(planquadrat: str) -> tuple[float, float]:
|
||||
"""Extrahiert X/Y Koordinaten aus PlanquadratString."""
|
||||
m = re.search(r"X:(\d+[\.,]?\d*)\s+Y:(\d+[\.,]?\d*)", planquadrat)
|
||||
@@ -87,8 +88,9 @@ def import_block(block_name: str, from_doc, to_doc) -> None:
|
||||
for ent in src:
|
||||
tgt.add_entity(ent.copy())
|
||||
|
||||
def berechne_hoehe(csv_path):
|
||||
y_werte = []
|
||||
def berechne_hoehe(csv_path, logger=None):
|
||||
y_werte = []
|
||||
try:
|
||||
with csv_path.open(newline="", encoding="utf-8") as fh:
|
||||
reader = csv.DictReader(fh, delimiter=';')
|
||||
for row in reader:
|
||||
@@ -96,11 +98,23 @@ def berechne_hoehe(csv_path):
|
||||
try:
|
||||
_, y = extract_coords(planquadrat)
|
||||
y_werte.append(y)
|
||||
except Exception:
|
||||
continue
|
||||
if not y_werte:
|
||||
raise ValueError("Keine Y-Koordinaten in der CSV gefunden!")
|
||||
return max(y_werte)
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.warning(f"Fehler beim Extrahieren der Koordinate aus '{planquadrat}': {e}")
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f"Fehler beim Lesen der CSV-Datei {csv_path}: {e}")
|
||||
else:
|
||||
print(f"Fehler beim Lesen der CSV-Datei {csv_path}: {e}")
|
||||
return 0
|
||||
if not y_werte:
|
||||
msg = "Keine Y-Koordinaten in der CSV gefunden!"
|
||||
if logger:
|
||||
logger.error(msg)
|
||||
else:
|
||||
print(msg)
|
||||
raise ValueError(msg)
|
||||
return max(y_werte)
|
||||
|
||||
def transform_coords(x: float, y: float, height: float) -> tuple[float, float]:
|
||||
"""Transformiert Bildschirmkoordinaten (0,0 oben links) ins DXF-KoSy (0,0 unten links)."""
|
||||
@@ -276,7 +290,7 @@ def normalize_func_name(name):
|
||||
|
||||
# --------------------------------------------------------- Hauptfunktion
|
||||
def main(csv_path: Path, lib_path: Path, cfg_path: Path,
|
||||
output_path: Path, verbose=False):
|
||||
output_path: Path, verbose=False, logger=None):
|
||||
|
||||
# Bibliothek nur laden, wenn Datei existiert
|
||||
lib_doc = None
|
||||
@@ -284,12 +298,21 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path,
|
||||
try:
|
||||
lib_doc = ezdxf.readfile(lib_path)
|
||||
if verbose:
|
||||
print(f"[INFO] Bibliothek geladen: {lib_path}")
|
||||
logger.info(f"[INFO] Bibliothek geladen: {lib_path}") if logger else print(f"[INFO] Bibliothek geladen: {lib_path}")
|
||||
except Exception as e:
|
||||
sys.exit(f"Fehler beim Lesen der Bibliothek '{lib_path}': {e}")
|
||||
msg = f"Fehler beim Lesen der Bibliothek '{lib_path}': {e}"
|
||||
if logger:
|
||||
logger.error(msg)
|
||||
else:
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"[INFO] Keine Bibliothek gefunden unter {lib_path}.")
|
||||
exit(1)
|
||||
msg = f"[INFO] Keine Bibliothek gefunden unter {lib_path}."
|
||||
if logger:
|
||||
logger.warning(msg)
|
||||
else:
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
|
||||
# Neue Zielzeichnung (DXF R2018)
|
||||
doc = ezdxf.new(dxfversion="R2018")
|
||||
@@ -297,7 +320,15 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path,
|
||||
|
||||
# CSV einlesen
|
||||
# Höhe bestimmen für Koordinaten-Transformation
|
||||
height = berechne_hoehe(csv_path)
|
||||
try:
|
||||
height = berechne_hoehe(csv_path, logger=logger)
|
||||
except Exception as e:
|
||||
msg = f"Fehler bei der Höhenberechnung: {e}"
|
||||
if logger:
|
||||
logger.error(msg)
|
||||
else:
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
|
||||
# Verarbeitung der Blöcke
|
||||
with csv_path.open(newline="", encoding="utf-8") as fh:
|
||||
@@ -312,22 +343,33 @@ def main(csv_path: Path, lib_path: Path, cfg_path: Path,
|
||||
x_screen, y_screen = extract_coords(planquadrat)
|
||||
x, y = transform_coords(x_screen, y_screen, height)
|
||||
except Exception as e:
|
||||
print(f"[WARN] {teileid}: {e}")
|
||||
msg = f"[WARN] {teileid}: {e}"
|
||||
if logger:
|
||||
logger.warning(msg)
|
||||
else:
|
||||
print(msg)
|
||||
continue
|
||||
|
||||
# Funktions-Dispatch: handle_<teileart> (mit _ statt Leerzeichen und Punkten, alles klein)
|
||||
func_name = f'handle_{normalize_func_name(teileart)}'
|
||||
handler = globals().get(func_name)
|
||||
symbols = get_shape_cfg(teileart, cfg_path)
|
||||
symbols = get_shape_cfg(teileart, cfg_path, logger=logger)
|
||||
if handler:
|
||||
handler(msp, teileid, merkmale, x, y, doc, lib_doc, verbose, symbols)
|
||||
else:
|
||||
print(f"[WARN] Keine Routine für TeileArt '{teileart}'. Überspringe '{teileid}'.")
|
||||
continue
|
||||
msg = f"[WARN] Keine Routine für TeileArt '{teileart}'. Überspringe '{teileid}'."
|
||||
if logger:
|
||||
logger.warning(msg)
|
||||
else:
|
||||
print(msg)
|
||||
continue
|
||||
|
||||
# DXF speichern
|
||||
doc.saveas(output_path)
|
||||
print(f"[DONE] DXF gespeichert unter: {output_path}")
|
||||
if logger:
|
||||
logger.info(f"[DONE] DXF gespeichert unter: {output_path}")
|
||||
else:
|
||||
print(f"[DONE] DXF gespeichert unter: {output_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -340,10 +382,14 @@ if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
|
||||
# Verzeichnisse aus Umgebungsvariablen
|
||||
log_dir = check_environment_var("PROJECT_LOG")
|
||||
data_dir = check_environment_var("PROJECT_DATA")
|
||||
work_dir = check_environment_var("PROJECT_WORK")
|
||||
config_dir = check_environment_var("PROJECT_CFG")
|
||||
|
||||
logger = setup_logger(log_dir, name='plant2dxf')
|
||||
logger.info("=== plant2dxf Verarbeitung gestartet ===")
|
||||
|
||||
# CSV‑Pfad: nur Dateiname → im WORK‑Ordner suchen
|
||||
if os.sep not in args.file and "/" not in args.file:
|
||||
csv_path = work_dir / args.file
|
||||
@@ -354,4 +400,5 @@ if __name__ == "__main__":
|
||||
lib_path = Path(args.lib) if args.lib else data_dir / "blocks.dxf"
|
||||
output_path = Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.dxf")
|
||||
|
||||
main(csv_path, lib_path, cfg_path, output_path, verbose=args.verbose)
|
||||
main(csv_path, lib_path, cfg_path, output_path, verbose=args.verbose, logger=logger)
|
||||
logger.info("=== plant2dxf Verarbeitung abgeschlossen ===")
|
||||
|
||||
+239
-24
@@ -3,14 +3,209 @@ import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
import time
|
||||
# Hilfsfunktion wie in plant2dxf.py
|
||||
import ezdxf
|
||||
from svg.path import parse_path, Line, Arc, CubicBezier, QuadraticBezier, Close
|
||||
import xml.etree.ElementTree as ET
|
||||
import re
|
||||
|
||||
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
|
||||
from utils import check_environment_var, setup_logger
|
||||
|
||||
class SVGtoDXFConverter:
|
||||
def __init__(self, logger=None):
|
||||
# Standard DXF-Farben (AutoCAD Color Index)
|
||||
self.color_map = {
|
||||
'red': 1, 'yellow': 2, 'green': 3, 'cyan': 4, 'blue': 5,
|
||||
'magenta': 6, 'white': 7, 'gray': 8, 'grey': 8, 'black': 7
|
||||
}
|
||||
self.logger = logger
|
||||
|
||||
def svg_to_dxf(self, svg_file: str, dxf_file: str):
|
||||
"""Konvertiert eine SVG-Datei zu DXF (AutoCAD 2018)"""
|
||||
try:
|
||||
# DXF-Dokument für AutoCAD 2018 erstellen
|
||||
doc = ezdxf.new('R2018')
|
||||
msp = doc.modelspace()
|
||||
|
||||
# SVG parsen
|
||||
tree = ET.parse(svg_file)
|
||||
root = tree.getroot()
|
||||
|
||||
# SVG-Namespace definieren
|
||||
namespaces = {
|
||||
'svg': 'http://www.w3.org/2000/svg',
|
||||
'': 'http://www.w3.org/2000/svg' # Default namespace
|
||||
}
|
||||
|
||||
# Alle path-Elemente finden und konvertieren
|
||||
for path_elem in root.iter():
|
||||
if path_elem.tag.endswith('path') or path_elem.tag == 'path':
|
||||
self.convert_path_element(path_elem, msp)
|
||||
|
||||
# DXF speichern
|
||||
doc.saveas(dxf_file)
|
||||
if self.logger:
|
||||
self.logger.info(f"DXF-Datei erfolgreich erstellt: {dxf_file}")
|
||||
else:
|
||||
print(f"DXF-Datei erfolgreich erstellt: {dxf_file}")
|
||||
except Exception as e:
|
||||
if self.logger:
|
||||
self.logger.error(f"Fehler beim Parsen der SVG-Datei {svg_file}: {e}")
|
||||
else:
|
||||
print(f"Fehler beim Parsen der SVG-Datei {svg_file}: {e}")
|
||||
|
||||
def convert_path_element(self, path_elem, msp):
|
||||
"""Konvertiert ein SVG path-Element zu DXF-Entities"""
|
||||
d = path_elem.get('d')
|
||||
if not d:
|
||||
return
|
||||
|
||||
# Farbe aus stroke-Attribut extrahieren
|
||||
color_index = self.get_color_from_stroke(path_elem.get('stroke', 'black'))
|
||||
|
||||
try:
|
||||
path = parse_path(d)
|
||||
self.convert_path_to_dxf(path, msp, color_index)
|
||||
except Exception as e:
|
||||
if self.logger:
|
||||
self.logger.warning(f"Fehler beim Parsen des Pfads: {e}")
|
||||
else:
|
||||
print(f"Warnung: Fehler beim Parsen des Pfads: {e}")
|
||||
|
||||
def get_color_from_stroke(self, stroke_value: str) -> int:
|
||||
"""Konvertiert SVG stroke-Farbe zu DXF Color Index"""
|
||||
if not stroke_value or stroke_value == 'none':
|
||||
return 7 # Standard: weiß/schwarz
|
||||
|
||||
stroke_value = stroke_value.lower().strip()
|
||||
|
||||
# Direkte Farbnamen
|
||||
if stroke_value in self.color_map:
|
||||
return self.color_map[stroke_value]
|
||||
|
||||
# Hex-Farben zu RGB und dann zu nächster DXF-Farbe
|
||||
if stroke_value.startswith('#'):
|
||||
return self.hex_to_dxf_color(stroke_value)
|
||||
|
||||
# RGB-Farben
|
||||
rgb_match = re.match(r'rgb\((\d+),\s*(\d+),\s*(\d+)\)', stroke_value)
|
||||
if rgb_match:
|
||||
r, g, b = map(int, rgb_match.groups())
|
||||
return self.rgb_to_dxf_color(r, g, b)
|
||||
|
||||
return 7 # Fallback: weiß/schwarz
|
||||
|
||||
def hex_to_dxf_color(self, hex_color: str) -> int:
|
||||
"""Konvertiert Hex-Farbe zu DXF Color Index"""
|
||||
hex_color = hex_color.lstrip('#')
|
||||
if len(hex_color) == 6:
|
||||
r = int(hex_color[0:2], 16)
|
||||
g = int(hex_color[2:4], 16)
|
||||
b = int(hex_color[4:6], 16)
|
||||
return self.rgb_to_dxf_color(r, g, b)
|
||||
return 7
|
||||
|
||||
def rgb_to_dxf_color(self, r: int, g: int, b: int) -> int:
|
||||
"""Konvertiert RGB-Werte zu nächstem DXF Color Index"""
|
||||
# Vereinfachte Zuordnung zu Hauptfarben
|
||||
if r > 200 and g < 100 and b < 100:
|
||||
return 1 # Rot
|
||||
elif r > 200 and g > 200 and b < 100:
|
||||
return 2 # Gelb
|
||||
elif r < 100 and g > 200 and b < 100:
|
||||
return 3 # Grün
|
||||
elif r < 100 and g > 200 and b > 200:
|
||||
return 4 # Cyan
|
||||
elif r < 100 and g < 100 and b > 200:
|
||||
return 5 # Blau
|
||||
elif r > 200 and g < 100 and b > 200:
|
||||
return 6 # Magenta
|
||||
elif r > 200 and g > 200 and b > 200:
|
||||
return 7 # Weiß
|
||||
else:
|
||||
return 8 # Grau
|
||||
|
||||
def convert_path_to_dxf(self, path, msp, color_index: int):
|
||||
"""Konvertiert SVG-Pfad zu DXF-Entities"""
|
||||
current_point = None
|
||||
|
||||
for segment in path:
|
||||
if isinstance(segment, Line):
|
||||
start = (segment.start.real, -segment.start.imag) # Y-Achse umkehren
|
||||
end = (segment.end.real, -segment.end.imag)
|
||||
line = msp.add_line(start, end)
|
||||
line.dxf.color = color_index
|
||||
current_point = end
|
||||
|
||||
elif isinstance(segment, Arc):
|
||||
# Arc zu Polyline approximieren
|
||||
points = self.arc_to_points(segment)
|
||||
if len(points) > 1:
|
||||
for i in range(len(points) - 1):
|
||||
start = (points[i].real, -points[i].imag)
|
||||
end = (points[i+1].real, -points[i+1].imag)
|
||||
line = msp.add_line(start, end)
|
||||
line.dxf.color = color_index
|
||||
current_point = (points[-1].real, -points[-1].imag)
|
||||
|
||||
elif isinstance(segment, (CubicBezier, QuadraticBezier)):
|
||||
# Bezier-Kurven zu Polyline approximieren
|
||||
points = self.bezier_to_points(segment)
|
||||
if len(points) > 1:
|
||||
for i in range(len(points) - 1):
|
||||
start = (points[i].real, -points[i].imag)
|
||||
end = (points[i+1].real, -points[i+1].imag)
|
||||
line = msp.add_line(start, end)
|
||||
line.dxf.color = color_index
|
||||
current_point = (points[-1].real, -points[-1].imag)
|
||||
|
||||
elif isinstance(segment, Close):
|
||||
# Pfad schließen - zurück zum Startpunkt
|
||||
if current_point and len(path) > 0:
|
||||
start_point = (path[0].start.real, -path[0].start.imag)
|
||||
if current_point != start_point:
|
||||
line = msp.add_line(current_point, start_point)
|
||||
line.dxf.color = color_index
|
||||
|
||||
def arc_to_points(self, arc, num_points: int = 20):
|
||||
"""Approximiert einen Bogen durch Punkte"""
|
||||
points = []
|
||||
for i in range(num_points + 1):
|
||||
t = i / num_points
|
||||
point = arc.point(t)
|
||||
points.append(point)
|
||||
return points
|
||||
|
||||
def bezier_to_points(self, bezier, num_points: int = 20):
|
||||
"""Approximiert eine Bezier-Kurve durch Punkte"""
|
||||
points = []
|
||||
for i in range(num_points + 1):
|
||||
t = i / num_points
|
||||
point = bezier.point(t)
|
||||
points.append(point)
|
||||
return points
|
||||
|
||||
# Batch-Konvertierung für mehrere Dateien
|
||||
def batch_convert(svg_files: list, output_dir: str = '.', logger=None):
|
||||
"""Konvertiert mehrere SVG-Dateien"""
|
||||
converter = SVGtoDXFConverter(logger=logger)
|
||||
|
||||
for svg_file in svg_files:
|
||||
try:
|
||||
# Ausgabedateiname generieren
|
||||
base_name = svg_file.rsplit('.', 1)[0]
|
||||
dxf_file = f"{output_dir}/{base_name}.dxf"
|
||||
|
||||
converter.svg_to_dxf(svg_file, dxf_file)
|
||||
if logger:
|
||||
logger.info(f"✓ {svg_file} -> {dxf_file}")
|
||||
else:
|
||||
print(f"✓ {svg_file} -> {dxf_file}")
|
||||
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f"✗ Fehler bei {svg_file}: {e}")
|
||||
else:
|
||||
print(f"✗ Fehler bei {svg_file}: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -18,7 +213,8 @@ if __name__ == "__main__":
|
||||
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('-o', '--output', type=str, help='Output-Verzeichnis für DXF-Dateien')
|
||||
|
||||
parser.add_argument('-k', '--inkscape', action='store_true', help='Batch-Konvertierung über Inkscape')
|
||||
|
||||
if len(sys.argv) == 2 and sys.argv[1] in ("-h", "--help"):
|
||||
parser.print_help()
|
||||
sys.exit(0)
|
||||
@@ -40,24 +236,43 @@ if __name__ == "__main__":
|
||||
OUTPUT_DIR = os.path.join(check_environment_var("PROJECT_WORK", "."), "converted_dxfs")
|
||||
print(f"Kein Output-Verzeichnis angegeben, verwende Standard: {OUTPUT_DIR} \n")
|
||||
|
||||
INKSCAPE = "inkscape" # ggf. vollständiger Pfad angeben
|
||||
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
files = [f for f in os.listdir(INPUT_DIR) if f.lower().endswith((".svg", ".xml"))]
|
||||
|
||||
for filename in files:
|
||||
input_path = os.path.join(INPUT_DIR, filename)
|
||||
output_path = os.path.join(OUTPUT_DIR, os.path.splitext(filename)[0] + ".dxf")
|
||||
log_dir = check_environment_var("PROJECT_LOG")
|
||||
logger = setup_logger(log_dir, name='svg2dxf')
|
||||
logger.info("=== SVG2DXF Verarbeitung gestartet ===")
|
||||
logger.info(f"Input-Verzeichnis: {INPUT_DIR}")
|
||||
logger.info(f"Output-Verzeichnis: {OUTPUT_DIR}")
|
||||
|
||||
|
||||
if args.inkscape:
|
||||
INKSCAPE = "inkscape" # ggf. vollständiger Pfad angeben
|
||||
for filename in files:
|
||||
input_path = os.path.join(INPUT_DIR, filename)
|
||||
output_path = os.path.join(OUTPUT_DIR, os.path.splitext(filename)[0] + ".dxf")
|
||||
if logger:
|
||||
logger.info(f"Konvertiere: {input_path} → {output_path}")
|
||||
else:
|
||||
print(f"Konvertiere: {input_path} → {output_path}")
|
||||
try:
|
||||
subprocess.run([
|
||||
INKSCAPE,
|
||||
input_path,
|
||||
"--export-type=dxf",
|
||||
f"--export-filename={output_path}"
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if logger:
|
||||
logger.error(f"[FEHLER] Inkscape-Absturz bei {filename}: {e}")
|
||||
else:
|
||||
print(f"[FEHLER] Inkscape-Absturz bei {filename}: {e}")
|
||||
time.sleep(0.5) # kleine Pause zur Entlastung von RAM/GTK
|
||||
|
||||
else:
|
||||
batch_convert(files, OUTPUT_DIR, logger=logger)
|
||||
|
||||
logger.info("=== SVG2DXF Verarbeitung abgeschlossen ===")
|
||||
|
||||
|
||||
print(f"Konvertiere: {input_path} → {output_path}")
|
||||
try:
|
||||
subprocess.run([
|
||||
INKSCAPE,
|
||||
input_path,
|
||||
"--export-type=dxf",
|
||||
f"--export-filename={output_path}"
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"[FEHLER] Inkscape-Absturz bei {filename}: {e}")
|
||||
time.sleep(0.5) # kleine Pause zur Entlastung von RAM/GTK
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def check_environment_var(env_str: str) -> Path:
|
||||
"""Liefert Path aus Umgebungsvariable oder beendet mit Fehlermeldung."""
|
||||
out_path = os.environ.get(env_str)
|
||||
if out_path:
|
||||
return Path(out_path)
|
||||
msg = f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer."
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def setup_logger(log_dir: Path, name: str = 'app') -> logging.Logger:
|
||||
"""
|
||||
Erstellt und konfiguriert einen Logger, der sowohl in eine Datei als auch auf die Konsole schreibt.
|
||||
Die Logdatei erhält einen Zeitstempel im Namen und ist UTF-8 kodiert.
|
||||
|
||||
Args:
|
||||
log_dir (Path): Verzeichnis für die Log-Dateien
|
||||
name (str): Name des Loggers (z.B. 'plant2dxf', 'dxf2lib')
|
||||
Returns:
|
||||
logging.Logger: Konfigurierter Logger
|
||||
"""
|
||||
from datetime import datetime
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
# Vorherige Handler entfernen, um doppelte Logs zu vermeiden
|
||||
for handler in logger.handlers[:]:
|
||||
logger.removeHandler(handler)
|
||||
|
||||
# Log-Verzeichnis anlegen (falls nicht vorhanden)
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Logdatei mit Zeitstempel im Namen erzeugen
|
||||
log_file = log_dir / f"{name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
||||
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
||||
file_handler.setLevel(logging.INFO)
|
||||
|
||||
# Handler für die Konsole
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
|
||||
# Einheitliches Log-Format definieren
|
||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||
file_handler.setFormatter(formatter)
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
# Handler dem Logger hinzufügen
|
||||
logger.addHandler(file_handler)
|
||||
logger.addHandler(console_handler)
|
||||
return logger
|
||||
Reference in New Issue
Block a user