131 lines
4.1 KiB
Python
131 lines
4.1 KiB
Python
import os
|
|
import re
|
|
import xml.etree.ElementTree as ET
|
|
import json
|
|
|
|
def parse_viewbox(root):
|
|
vb = root.get('viewBox')
|
|
if vb:
|
|
parts = [float(x) for x in vb.split()]
|
|
if len(parts) == 4:
|
|
return parts[2], parts[3] # Width, Height
|
|
width = root.get('width')
|
|
height = root.get('height')
|
|
if width and height:
|
|
return float(width.replace('in','').replace('px','')) * 96, float(height.replace('in','').replace('px','')) * 96 # rough approx if units
|
|
return 30.0, 40.0 # Fallback
|
|
|
|
def tokenize_path(d):
|
|
# Split by commands and numbers.
|
|
# Commands: M, m, L, l, V, v, H, h, C, c, S, s, Q, q, T, t, A, a, Z, z
|
|
# Numbers: float regex
|
|
tokens = re.findall(r'[a-zA-Z]|[-+]?(?:\d*\.\d+|\d+)(?:[eE][-+]?\d+)?', d)
|
|
return tokens
|
|
|
|
def is_num(t):
|
|
try:
|
|
float(t)
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
def shift_d(d, dx, dy):
|
|
tokens = tokenize_path(d)
|
|
new_tokens = []
|
|
|
|
current_cmd = ''
|
|
i = 0
|
|
|
|
# SVG 1.1: first command must be M or m. m treated as absolute M.
|
|
|
|
while i < len(tokens):
|
|
t = tokens[i]
|
|
if not is_num(t):
|
|
current_cmd = t
|
|
new_tokens.append(t)
|
|
i += 1
|
|
|
|
# Special case for 'm' at start: first pair absolute, rest relative
|
|
if current_cmd == 'm' and len(new_tokens) == 1:
|
|
# First pair
|
|
if i+1 < len(tokens):
|
|
x = float(tokens[i]) + dx
|
|
y = float(tokens[i+1]) + dy
|
|
new_tokens.extend([f"{x:.4f}", f"{y:.4f}"])
|
|
i += 2
|
|
# Subsequent pairs are relative line-tos, do NOT shift
|
|
|
|
continue
|
|
|
|
# It's a number, implies continuation of previous command or implicit line-to
|
|
# If we are here, we are processing parameters for current_cmd
|
|
|
|
# Determine if absolute coordinate
|
|
if current_cmd in ['M', 'L', 'C', 'S', 'Q', 'T']:
|
|
# Pairs of X, Y
|
|
x = float(tokens[i]) + dx
|
|
y = float(tokens[i+1]) + dy
|
|
new_tokens.extend([f"{x:.4f}", f"{y:.4f}"])
|
|
i += 2
|
|
elif current_cmd == 'V':
|
|
y = float(tokens[i]) + dy
|
|
new_tokens.append(f"{y:.4f}")
|
|
i += 1
|
|
elif current_cmd == 'H':
|
|
x = float(tokens[i]) + dx
|
|
new_tokens.append(f"{x:.4f}")
|
|
i += 1
|
|
elif current_cmd == 'A':
|
|
# rx ry rot large sweep x y
|
|
# skip 5
|
|
new_tokens.extend(tokens[i:i+5])
|
|
i += 5
|
|
x = float(tokens[i]) + dx
|
|
y = float(tokens[i+1]) + dy
|
|
new_tokens.extend([f"{x:.4f}", f"{y:.4f}"])
|
|
i += 2
|
|
else:
|
|
# Relative commands (m, l, c, s, q, t, v, h, a) - do NOT shift
|
|
# Just copy tokens until next command
|
|
# But we need to know how many args to consume?
|
|
# Easier: just iterate until next alpha token
|
|
new_tokens.append(t)
|
|
i += 1
|
|
|
|
return ' '.join(new_tokens)
|
|
|
|
base_dir = 'output_webp'
|
|
final_paths = {}
|
|
|
|
for num in range(10):
|
|
filename = os.path.join(base_dir, f'{num}.svg')
|
|
if os.path.exists(filename):
|
|
try:
|
|
tree = ET.parse(filename)
|
|
root = tree.getroot()
|
|
|
|
w, h = parse_viewbox(root)
|
|
cx, cy = w/2, h/2
|
|
|
|
# Find path
|
|
ns = {'svg': 'http://www.w3.org/2000/svg'}
|
|
path = root.find('.//svg:path', ns)
|
|
if not path:
|
|
path = root.find('.//{http://www.w3.org/2000/svg}path')
|
|
if not path:
|
|
for elem in root.iter():
|
|
if elem.tag.endswith('path'):
|
|
path = elem
|
|
break
|
|
|
|
if path is not None:
|
|
d = path.get('d')
|
|
if d:
|
|
# Shift so center is 0,0
|
|
shifted = shift_d(d, -cx, -cy)
|
|
final_paths[str(num)] = shifted
|
|
except Exception as e:
|
|
print(f"Error {num}: {e}")
|
|
|
|
print(json.dumps(final_paths))
|