Spaces:
Sleeping
Sleeping
| """ | |
| SVG optimization functionality. | |
| """ | |
| import re | |
| import xml.etree.ElementTree as ET | |
| from loguru import logger | |
| def calculate_path_area(path_data): | |
| """ | |
| Estimate the area of a path by calculating its bounding box. | |
| Args: | |
| path_data (str): SVG path data string | |
| Returns: | |
| float: Estimated area of the path | |
| """ | |
| # Extract coordinates from path data | |
| coords = re.findall(r'[A-Z]\s*(-?\d+\.?\d*)\s+(-?\d+\.?\d*)', path_data) | |
| if not coords: | |
| # Try to extract coordinates from more complex path data | |
| coords = re.findall(r'(-?\d+\.?\d*)\s+(-?\d+\.?\d*)', path_data) | |
| if not coords: | |
| return 0 | |
| # Convert to float | |
| coords = [(float(x), float(y)) for x, y in coords] | |
| # Find bounding box | |
| min_x = min(x for x, _ in coords) | |
| max_x = max(x for x, _ in coords) | |
| min_y = min(y for _, y in coords) | |
| max_y = max(y for _, y in coords) | |
| # Calculate area | |
| width = max_x - min_x | |
| height = max_y - min_y | |
| return width * height | |
| def optimize_svg(svg_path, min_area_percentage=0.01): | |
| """ | |
| Optimize SVG by removing tiny paths that are likely noise. | |
| Args: | |
| svg_path (str): Path to the SVG file | |
| min_area_percentage (float): Minimum area as percentage of total SVG area | |
| Returns: | |
| str: Path to the optimized SVG file | |
| """ | |
| try: | |
| # Parse the SVG file | |
| tree = ET.parse(svg_path) | |
| root = tree.getroot() | |
| # Find all path elements | |
| paths = root.findall(".//{http://www.w3.org/2000/svg}path") | |
| if not paths: | |
| logger.warning(f"No paths found in {svg_path}") | |
| return svg_path | |
| # Get SVG dimensions | |
| width = float(root.get('width', '100')) | |
| height = float(root.get('height', '100')) | |
| total_area = width * height | |
| min_area = total_area * min_area_percentage / 100 | |
| # Count paths before optimization | |
| path_count_before = len(paths) | |
| # Remove tiny paths | |
| paths_to_remove = [] | |
| for path in paths: | |
| path_data = path.get('d', '') | |
| area = calculate_path_area(path_data) | |
| if area < min_area: | |
| paths_to_remove.append(path) | |
| for path in paths_to_remove: | |
| parent = path.getparent() | |
| if parent is not None: | |
| parent.remove(path) | |
| # Count paths after optimization | |
| paths_after = root.findall(".//{http://www.w3.org/2000/svg}path") | |
| path_count_after = len(paths_after) | |
| logger.info(f"Optimized SVG: removed {path_count_before - path_count_after} of {path_count_before} paths") | |
| # Save the optimized SVG | |
| optimized_path = svg_path.replace('.svg', '_optimized.svg') | |
| tree.write(optimized_path) | |
| return optimized_path | |
| except Exception as e: | |
| logger.error(f"Error optimizing SVG: {str(e)}") | |
| return svg_path | |
| def adjust_viewbox(root, paths): | |
| """ | |
| Adjust SVG viewBox to fit the content. | |
| Args: | |
| root (ElementTree.Element): SVG root element | |
| paths (list): List of path elements | |
| Returns: | |
| tuple: (min_x, min_y, width, height) | |
| """ | |
| if not paths: | |
| return 0, 0, 100, 100 | |
| # Initialize bounds | |
| min_x = min_y = float('inf') | |
| max_x = max_y = float('-inf') | |
| # Calculate bounds from all paths | |
| for path in paths: | |
| path_data = path.get('d', '') | |
| coords = re.findall(r'[A-Z]\s*(-?\d+\.?\d*)\s+(-?\d+\.?\d*)', path_data) | |
| if not coords: | |
| coords = re.findall(r'(-?\d+\.?\d*)\s+(-?\d+\.?\d*)', path_data) | |
| if coords: | |
| xs = [float(x) for x, _ in coords] | |
| ys = [float(y) for _, y in coords] | |
| min_x = min(min_x, min(xs)) | |
| max_x = max(max_x, max(xs)) | |
| min_y = min(min_y, min(ys)) | |
| max_y = max(max_y, max(ys)) | |
| # Add padding | |
| padding = 10 | |
| min_x = max(0, min_x - padding) | |
| min_y = max(0, min_y - padding) | |
| width = max_x - min_x + 2 * padding | |
| height = max_y - min_y + 2 * padding | |
| return min_x, min_y, width, height | |