import re import os import xml.etree.ElementTree as ET from typing import List, Dict, Tuple def process_svg_file(input_path: str, output_path: str) -> bool: """Process SVG file while preserving original dimensions and viewBox""" try: # Parse SVG file tree = ET.parse(input_path) root = tree.getroot() # Store original dimensions and viewBox original_attrs = { 'width': root.attrib.get('width', '100%'), 'height': root.attrib.get('height', '100%'), 'viewBox': root.attrib.get('viewBox', '') } # Remove namespace prefixes for elem in root.iter(): if '}' in elem.tag: elem.tag = elem.tag.split('}', 1)[1] for attr in list(elem.attrib): if '}' in attr: new_attr = attr.split('}', 1)[1] elem.attrib[new_attr] = elem.attrib[attr] del elem.attrib[attr] # Process all groups with transforms for g in root.findall('g'): if 'transform' in g.attrib: # Parse transform and convert to matrix transform = g.attrib['transform'] matrix_str = convert_transform_to_matrix(transform) # Update group attributes g.attrib['transform'] = matrix_str g.attrib['stroke'] = '#ffe31b' # Force stroke color g.attrib['fill'] = 'none' g.attrib['stroke-linecap'] = 'square' g.attrib['vector-effect'] = 'non-scaling-stroke' # Format paths within group for path in g.findall('path'): path.attrib['d'] = simplify_path_data(path.attrib.get('d', '')) path.tail = '\n ' # Maintain consistent indentation # Set root attributes while preserving original dimensions root.attrib.update({ 'version': '1.1', 'xmlns': 'http://www.w3.org/2000/svg', 'width': original_attrs['width'], 'height': original_attrs['height'], 'viewBox': original_attrs['viewBox'], 'fill-rule': 'evenodd', 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'space': 'preserve' }) # Format XML with proper indentation indent(root) # Generate XML string xml_str = ET.tostring(root, encoding='unicode') xml_str = '\n' + xml_str # Clean up formatting xml_str = re.sub(r'\n\s*\n', '\n', xml_str) xml_str = re.sub(r'>\s+<', '>\n<', xml_str) # Write to file with open(output_path, 'w', encoding='utf-8') as f: f.write(xml_str) print(f"Processed: {os.path.basename(input_path)}") return True except Exception as e: print(f"Failed {os.path.basename(input_path)}: {str(e)}") return False def convert_transform_to_matrix(transform: str) -> str: """Convert transform string to matrix notation with 4 decimal places""" tx = ty = 0.0 translate_match = re.search(r'translate\(([^,]+),\s*([^)]+)\)', transform) if translate_match: tx = float(translate_match.group(1)) ty = float(translate_match.group(2)) scale = 1.0 scale_match = re.search(r'scale\(([^)]+)\)', transform) if scale_match: scale = float(scale_match.group(1)) return f"matrix({scale:.4f} 0 0 {scale:.4f} {tx:.2f} {ty:.2f})" def simplify_path_data(d: str) -> str: """Simplify path data while maintaining relative commands""" d = re.sub(r'\s+', ' ', d.strip()) def format_number(num_str: str) -> str: try: num = float(num_str) if abs(num) < 0.001 and num != 0: return f"{num:.0e}".replace('e-0', 'e-') if abs(num - 1000) < 0.1: return '1e3' if abs(num - round(num, 3)) < 0.0001: return f"{round(num, 3):g}" return num_str except ValueError: return num_str parts = [] for part in re.split(r'([ ,])', d): if re.match(r'^[-+]?\d*\.?\d+$', part): parts.append(format_number(part)) else: parts.append(part) return ''.join(parts).replace(' ,', ',') def indent(elem: ET.Element, level: int = 0): """Properly indent XML elements""" indent_str = "\n" + level * " " if len(elem): if not elem.text or not elem.text.strip(): elem.text = indent_str + " " if not elem.tail or not elem.tail.strip(): elem.tail = indent_str for child in elem: indent(child, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = indent_str else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = indent_str def batch_process_svgs(input_dir: str, output_dir: str): """Process all SVG files in a directory""" if not os.path.exists(output_dir): os.makedirs(output_dir) success_count = 0 for filename in sorted(os.listdir(input_dir)): if filename.lower().endswith('.svg'): input_path = os.path.join(input_dir, filename) output_path = os.path.join(output_dir, filename) if process_svg_file(input_path, output_path): success_count += 1 print(f"\nCompleted: {success_count} files processed") if __name__ == '__main__': input_dir = r'C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\Weichen' output_dir = r'C:\Users\y.wang\Documents\SSG-Ruledesigner-Konfigurator\SVGs\Omniflo\Weichen\outputdir' # Example usage: batch_process_svgs(input_dir, output_dir)