mirror of
https://github.com/GoodStartLabs/AI_Diplomacy.git
synced 2026-04-19 12:58:09 +00:00
Moved all the files that don't need to be at top level to the experiments folder. Started using uv to init the project, should the install easier.
169 lines
No EOL
5.9 KiB
Python
169 lines
No EOL
5.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
SVG Optimizer - A tool to simplify and optimize SVG files
|
|
|
|
This module provides functions to clean up SVG files by:
|
|
1. Removing unnecessary metadata, comments, and custom namespaces
|
|
2. Simplifying paths using the svgelements library
|
|
3. Standardizing dimensions with viewBox
|
|
4. Removing inline styles and unnecessary attributes
|
|
|
|
Author: AI Assistant
|
|
"""
|
|
|
|
import xml.etree.ElementTree as ET
|
|
import io
|
|
import re
|
|
import os
|
|
from svgelements import Path
|
|
|
|
def simplify_svg(svg_content):
|
|
"""
|
|
Simplifies and optimizes SVG content by:
|
|
1. Removing unnecessary metadata, comments, and hidden elements.
|
|
2. Simplifying paths and shapes.
|
|
3. Standardizing dimensions (setting viewBox).
|
|
|
|
Args:
|
|
svg_content (str): The SVG content as a string
|
|
|
|
Returns:
|
|
str: The optimized SVG content
|
|
"""
|
|
# Remove comments before parsing (ElementTree doesn't handle comments well)
|
|
svg_content = re.sub(r'<!--[\s\S]*?-->', '', svg_content)
|
|
|
|
# Parse the SVG content
|
|
try:
|
|
# Register SVG namespaces to avoid prefix generation
|
|
ET.register_namespace('', 'http://www.w3.org/2000/svg')
|
|
ET.register_namespace('xlink', 'http://www.w3.org/1999/xlink')
|
|
|
|
# Parse the SVG
|
|
tree = ET.parse(io.StringIO(svg_content))
|
|
root = tree.getroot()
|
|
except ET.ParseError as e:
|
|
return f"Error parsing SVG: {e}"
|
|
|
|
# 1. Remove metadata, custom elements, and unnecessary elements
|
|
for element in list(root): # Iterate over a copy to allow modification
|
|
tag = element.tag
|
|
if (tag.endswith('}DISPLAY') or
|
|
tag.endswith('}ORDERDRAWING') or
|
|
tag.endswith('}PROVINCE_DATA') or
|
|
tag == '{http://www.w3.org/2000/svg}defs' or
|
|
tag == '{http://www.w3.org/2000/svg}metadata' or
|
|
tag == '{http://www.w3.org/2000/svg}style'):
|
|
root.remove(element)
|
|
|
|
# Remove jdipNS namespace declaration and attributes as they are custom
|
|
jdipns_prefix = '{svg.dtd}'
|
|
for element in root.iter():
|
|
attrib_to_remove = []
|
|
for attrib_name in element.attrib:
|
|
if attrib_name.startswith('{svg.dtd}'):
|
|
attrib_to_remove.append(attrib_name)
|
|
for attrib_name in attrib_to_remove:
|
|
del element.attrib[attrib_name]
|
|
|
|
# 2. Simplify paths and shapes
|
|
for path_element in root.findall('.//{http://www.w3.org/2000/svg}path'):
|
|
d_attribute = path_element.get('d')
|
|
if d_attribute:
|
|
try:
|
|
# Parse the path data
|
|
path = Path(d_attribute)
|
|
|
|
# Simplify the path using svgelements
|
|
# First convert all arcs to cubic bezier curves for better handling
|
|
path.approximate_arcs_with_cubics()
|
|
|
|
# Basic path simplification - remove redundant commands
|
|
simplified_d = path.d()
|
|
|
|
# Set the simplified path back
|
|
path_element.set('d', simplified_d)
|
|
except Exception as e:
|
|
print(f"Path simplification error for path {path_element.get('id', 'unknown')}: {e}")
|
|
# Keep the original path if simplification fails
|
|
continue
|
|
|
|
# 3. Standardize dimensions with viewBox
|
|
if 'viewBox' not in root.attrib:
|
|
# Try to get width and height
|
|
width = root.get('width')
|
|
height = root.get('height')
|
|
|
|
if width and height:
|
|
# Extract numeric values
|
|
width_value = re.match(r'(\d+)', width)
|
|
height_value = re.match(r'(\d+)', height)
|
|
|
|
if width_value and height_value:
|
|
width_num = float(width_value.group(1))
|
|
height_num = float(height_value.group(1))
|
|
root.set('viewBox', f"0 0 {width_num} {height_num}")
|
|
|
|
# Remove width and height if viewBox is present
|
|
if 'viewBox' in root.attrib:
|
|
if 'width' in root.attrib:
|
|
del root.attrib['width']
|
|
if 'height' in root.attrib:
|
|
del root.attrib['height']
|
|
|
|
# 4. Export as clean SVG code snippet
|
|
output_io = io.StringIO()
|
|
tree.write(output_io, encoding='unicode', xml_declaration=True, short_empty_elements=True)
|
|
|
|
# Get the output and clean it up
|
|
optimized_svg_code = output_io.getvalue()
|
|
|
|
# Remove DOCTYPE from string output as ElementTree doesn't fully remove it
|
|
optimized_svg_code = re.sub(r'<!DOCTYPE[^>]*>', '', optimized_svg_code)
|
|
|
|
# Remove comments that might have been missed by ElementTree
|
|
optimized_svg_code = re.sub(r'<!--[\s\S]*?-->', '', optimized_svg_code)
|
|
|
|
return optimized_svg_code.strip()
|
|
|
|
|
|
def find_parent(root, element):
|
|
"""
|
|
Find the parent of an element in the XML tree
|
|
|
|
Args:
|
|
root (Element): The root element of the tree
|
|
element (Element): The element to find the parent for
|
|
|
|
Returns:
|
|
Element: The parent element or None if not found
|
|
"""
|
|
for parent in root.iter():
|
|
for child in list(parent):
|
|
if child == element:
|
|
return parent
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Test the function with a sample SVG
|
|
test_svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "svg.dtd">
|
|
<!-- Comment to be removed -->
|
|
<svg color-rendering="optimizeQuality" height="680px" preserveAspectRatio="xMinYMin" version="1.0" viewBox="0 0 1835 1360" width="918px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:jdipNS="svg.dtd">
|
|
<jdipNS:DISPLAY>
|
|
<jdipNS:ZOOM min="5" max="2200" factor="1.2"/>
|
|
</jdipNS:DISPLAY>
|
|
<defs>
|
|
<style type="text/css"><![CDATA[
|
|
/* text */
|
|
svg { font-size: 100% }
|
|
]]></style>
|
|
</defs>
|
|
<g id="MapLayer">
|
|
<path id="test" d="M 10 10 L 20 20 L 30 30 Z"/>
|
|
</g>
|
|
</svg>"""
|
|
|
|
optimized = simplify_svg(test_svg)
|
|
print(optimized) |