relevante Datei für 2D Library Erzeugung
This commit is contained in:
@@ -375,7 +375,7 @@ def optimize_svg(input_path, output_path):
|
||||
elem.set('stroke', '#ffe31b')
|
||||
elif stroke == '#000' or stroke == 'rgb(255,0,0)'or stroke == 'rgb(0,0,0)':
|
||||
elem.set('stroke', '#ffe31b')
|
||||
elem.set('stroke-width', '1')
|
||||
elem.set('stroke-width', f'{1}px')
|
||||
|
||||
# Generate final XML
|
||||
rough_string = ET.tostring(root, encoding='utf-8', xml_declaration=True)
|
||||
|
||||
@@ -4,8 +4,7 @@ import xml.etree.ElementTree as ET
|
||||
from typing import Tuple, List
|
||||
|
||||
def parse_svg_path(d: str) -> List[Tuple[float, float]]:
|
||||
"""Extract all coordinates from SVG path data (assumes all units are converted to px)"""
|
||||
d = re.sub(r'(?<=\d)(mm|pt|cm|in|px)\b', '', d)
|
||||
"""Extract all coordinates from SVG path data (including curve control points)"""
|
||||
points = []
|
||||
commands = re.findall(
|
||||
r'([MmLlCcQqAaHhVvZz])([^MmLlCcQqAaHhVvZz]*)',
|
||||
@@ -43,11 +42,9 @@ def parse_svg_path(d: str) -> List[Tuple[float, float]]:
|
||||
return points
|
||||
|
||||
def calculate_bounding_box(svg_file: str) -> Tuple[float, float, float, float]:
|
||||
"""Calculate true bounding box of all paths in SVG (assumes all coordinates are in px)"""
|
||||
"""Calculate true bounding box of all paths in SVG"""
|
||||
tree = ET.parse(svg_file)
|
||||
root = tree.getroot()
|
||||
|
||||
|
||||
all_points = []
|
||||
|
||||
for path in root.findall('.//{http://www.w3.org/2000/svg}path'):
|
||||
@@ -64,7 +61,6 @@ def calculate_bounding_box(svg_file: str) -> Tuple[float, float, float, float]:
|
||||
return (x_min, y_min, x_max, y_max)
|
||||
|
||||
|
||||
|
||||
def normalize_stroke_widths2(root, scale):
|
||||
"""Normalize stroke widths to ensure consistent appearance after scaling"""
|
||||
for elem in root.iter():
|
||||
@@ -75,103 +71,65 @@ def normalize_stroke_widths2(root, scale):
|
||||
elem.set('stroke-width', f'{1/scale:.6f}mm')
|
||||
|
||||
def apply_non_scaling_stroke(root):
|
||||
"""Ensure stroke widths don't scale with the SVG"""
|
||||
"""确保所有有描边的元素的描边宽度不随缩放变化"""
|
||||
for elem in root.iter():
|
||||
if 'stroke' in elem.attrib and elem.attrib['stroke'] != 'none':
|
||||
# 强制设置 stroke-width=1(无单位)
|
||||
elem.set('stroke-width', '1')
|
||||
# 确保 vector-effect 生效
|
||||
elem.set('vector-effect', 'non-scaling-stroke')
|
||||
# 递归清理所有<g>的transform
|
||||
def remove_all_transforms(root):
|
||||
for g in root.findall('.//{http://www.w3.org/2000/svg}g'):
|
||||
if 'transform' in g.attrib:
|
||||
del g.attrib['transform']
|
||||
|
||||
def scale_svg_file(input_path: str, output_path: str):
|
||||
# 手动移除ns0前缀(字符串替换)
|
||||
with open(input_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
content = content.replace('ns0:', '').replace(':ns0=', '=')
|
||||
tree = ET.ElementTree(ET.fromstring(content))
|
||||
"""Scale SVG file with consistent stroke widths"""
|
||||
print(f"\nProcessing: {os.path.basename(input_path)}")
|
||||
|
||||
tree = ET.parse(input_path)
|
||||
root = tree.getroot()
|
||||
|
||||
# Calculate bounding box INCLUDING stroke width
|
||||
# Calculate original bounding box
|
||||
x_min, y_min, x_max, y_max = calculate_bounding_box(input_path)
|
||||
width = x_max - x_min
|
||||
height = y_max - y_min
|
||||
print(f"Old dimensions: {width:.3f} × {height:.3f}")
|
||||
# Determine scale factor (larger dimension → 1000px)
|
||||
scale = 1000.0 / max(width, height)
|
||||
print(f"Scale factor: {scale:.6f}")
|
||||
remove_all_transforms(root)
|
||||
|
||||
for path in root.findall('.//{http://www.w3.org/2000/svg}path'):
|
||||
d = path.get('d', '')
|
||||
d = re.sub(r'(?<=\d)(mm|px|cm|in)', '', d) # 匹配 "10mm" → "10"
|
||||
scaled_d = scale_svg_path(d, scale, (-x_min, -y_min))
|
||||
path.set('d', scaled_d)
|
||||
for g in root.findall('.//{http://www.w3.org/2000/svg}g'):
|
||||
if 'transform' in g.attrib:
|
||||
del g.attrib['transform']
|
||||
new_width = round(width * scale,3)
|
||||
new_height = round(height * scale)
|
||||
print(f"Original bounding box: ({x_min:.3f}, {y_min:.3f}) to ({x_max:.3f}, {y_max:.3f})")
|
||||
print(f"Original dimensions: {width:.3f} (w) × {height:.3f} (h)")
|
||||
|
||||
|
||||
# Determine scaling base (larger dimension → 1000px)
|
||||
if width > height:
|
||||
scale = 1000.0 / width
|
||||
new_width = 1000.0
|
||||
new_height = round(height * scale)
|
||||
print(f"Scaling base: Width (larger dimension)")
|
||||
else:
|
||||
scale = 1000.0 / height
|
||||
new_height = 1000.0
|
||||
new_width = round(width * scale,3)
|
||||
print(f"Scaling base: Height (larger dimension)")
|
||||
|
||||
print(f"Scale factor: {scale:.6f}")
|
||||
print(f"New dimensions: {new_width:.3f}px × {new_height:.3f}px")
|
||||
print(f"ViewBox: 0 0 {new_width:.3f} {new_height:.3f}")
|
||||
# apply_non_scaling_stroke(root)
|
||||
# normalize_stroke_widths2(root, scale)
|
||||
# normalize_stroke_widths2(root, scale)
|
||||
apply_non_scaling_stroke(root)
|
||||
#
|
||||
# Update SVG attributes
|
||||
root.set('viewBox', f'0 0 {new_width:.3f} {new_height:.3f}')
|
||||
root.set('width', f'{new_width:.3f}px')
|
||||
root.set('height', f'{new_height:.3f}px')
|
||||
root.set('width', f'{new_width:.3f}')
|
||||
root.set('height', f'{new_height:.3f}')
|
||||
|
||||
# Apply translation and scaling
|
||||
# for g in root.findall('{http://www.w3.org/2000/svg}g'):
|
||||
# transform = g.get('transform', '')
|
||||
# new_transform = f'translate({-x_min*scale},{-y_min*scale}) scale({scale})'
|
||||
# if transform:
|
||||
# new_transform = f'{transform} {new_transform}'
|
||||
# g.set('transform', new_transform)
|
||||
for g in root.findall('{http://www.w3.org/2000/svg}g'):
|
||||
transform = g.get('transform', '')
|
||||
new_transform = f'translate({-x_min*scale},{-y_min*scale}) scale({scale})'
|
||||
if transform:
|
||||
new_transform = f'{transform} {new_transform}'
|
||||
g.set('transform', new_transform)
|
||||
|
||||
# Save modified SVG
|
||||
ET.register_namespace('', 'http://www.w3.org/2000/svg')
|
||||
tree.write(output_path, encoding='utf-8', xml_declaration=True)
|
||||
print(f"Finished processing: {os.path.basename(input_path)}")
|
||||
|
||||
def scale_svg_path(d: str, scale: float, offset: Tuple[float, float]) -> str:
|
||||
d = re.sub(r'(?<=\d)(mm|pt|cm|in|px)\b', '', d)
|
||||
commands = re.findall(
|
||||
r'([MmLlCcQqAaHhVvZz])([^MmLlCcQqAaHhVvZz]*)',
|
||||
d.strip().replace(',', ' ')
|
||||
)
|
||||
scaled_parts = []
|
||||
offset_x, offset_y = offset
|
||||
|
||||
for cmd, args in commands:
|
||||
nums = list(map(float, re.findall(r'[-+]?\d*\.?\d+', args)))
|
||||
scaled_nums = []
|
||||
i= 0
|
||||
while i < len(nums):
|
||||
if cmd in ('A', 'a'): # 圆弧命令特殊处理
|
||||
# rx ry x-axis-rotation large-arc-flag sweep-flag x y
|
||||
scaled_nums.extend([
|
||||
f"{nums[i] * scale:.3f}", # rx
|
||||
f"{nums[i+1] * scale:.3f}", # ry
|
||||
f"{nums[i+2]:.3f}", # x-axis-rotation (不缩放)
|
||||
f"{int(nums[i+3])}", # large-arc-flag
|
||||
f"{int(nums[i+4])}", # sweep-flag
|
||||
f"{(nums[i+5] + (offset_x if cmd == 'a' else 0)) * scale:.3f}", # x
|
||||
f"{(nums[i+6] + (offset_y if cmd == 'a' else 0)) * scale:.3f}" # y
|
||||
])
|
||||
i += 7
|
||||
else: # 其他命令
|
||||
if i % 2 == 0: # x坐标
|
||||
val = (nums[i] + (offset_x if cmd.islower() else 0)) * scale
|
||||
else: # y坐标
|
||||
val = (nums[i] + (offset_y if cmd.islower() else 0)) * scale
|
||||
scaled_nums.append(f"{val:.3f}")
|
||||
i += 1
|
||||
|
||||
scaled_part = cmd + ' '.join(scaled_nums)
|
||||
scaled_parts.append(scaled_part.replace(' -', '-')) # 优化负号格式
|
||||
|
||||
return ' '.join(scaled_parts).replace(' ', ' ')
|
||||
def batch_process_svg(input_dir: str, output_dir: str):
|
||||
"""Batch process SVG files in directory (non-recursive)"""
|
||||
if not os.path.exists(output_dir):
|
||||
|
||||
Reference in New Issue
Block a user