File size: 4,371 Bytes
ad8cacf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""

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