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 | |