如何用Python调整SVG至512×512尺寸且保持视觉效果不变?
需求:将任意SVG批量调整为512×512规格并保持视觉效果
需要将任意SVG文件调整为以下固定参数:
width = '512' height = '512' viewBox = '0 0 512 512'
同时需重绘矢量内容,确保缩放后图像视觉效果完全一致。例如原SVG参数为width="800px"、height="800px"、viewBox="0 0 1024 1024"时,需修改参数并按比例修正所有矢量元素的坐标、尺寸,保证显示效果不变。
已尝试的无效方法
1. svgutils库
仅能修改宽高参数,无法同步更新viewBox及矢量元素尺寸:
import os import svgutils.transform as sg directory = os.path.dirname(os.path.realpath(__file__)) for filename in os.listdir(directory): if filename.endswith('.svg'): fig = sg.fromfile(os.path.join(directory, filename)) fig.set_size(('200', '200')) fig.save(os.path.join(directory, 'resized_' + filename))
2. Inkscape命令行
使用--export-width=512和--export-height=512参数未达到预期效果:
import subprocess import glob import os # Get the directory of the current script directory = os.path.dirname(os.path.realpath(__file__)) # Get a list of all SVG files in the directory svg_files = glob.glob(os.path.join(directory, '*.svg')) inkscape_path = 'D:\\Apps\\Inkscape\\bin\\inkscape.exe' for file in svg_files: # Export the file as a new SVG file with a width and height of 512 output_file = f"{file.rsplit('.', 1)[0]}_resized.svg" result = subprocess.run([inkscape_path, file, '--export-area-drawing', '--export-filename=' + output_file, '--export-width=512', '--export-height=512'], capture_output=True, text=True, errors='ignore') print(result.stdout) print(result.stderr)
3. Cairo + rsvg库
Windows系统下报错AttributeError: module 'rsvg' has no attribute 'Handle':
import os import cairo import rsvg WIDTH, HEIGHT = 512, 512# desired width and height # Get the directory of the current script directory = os.path.dirname(os.path.realpath(__file__)) # Use os.walk to find all SVG files for root, dirs, files in os.walk(directory): for file in files: if file.endswith('.svg'): svg_path = os.path.join(root, file) png_path = os.path.splitext(svg_path)[0] + '.png' # Load the SVG data with open(svg_path, 'r') as svg_xml: svg = rsvg.Handle() svg.write(svg_xml.read()) svg.close() # Prepare the Cairo context img = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) ctx = cairo.Context(img) # Scale whatever is written into this context ctx.scale(2, 2) # scale in x and y directions svg.render_cairo(ctx) # Write out into a PNG file with open(png_path, 'wb') as fout: fout.write(img.write_to_png())
可行的Python实现方案
以下方案通过直接解析SVG的XML结构,计算缩放比例后批量调整所有矢量元素的属性,确保最终SVG符合要求且视觉效果不变:
import os import xml.etree.ElementTree as ET from xml.dom import minidom # 目标参数 TARGET_WIDTH = 512 TARGET_HEIGHT = 512 TARGET_VIEWBOX = f"0 0 {TARGET_WIDTH} {TARGET_HEIGHT}" def process_svg(input_path, output_path): # 解析SVG文件 tree = ET.parse(input_path) root = tree.getroot() # 获取SVG命名空间 ns = {'svg': 'http://www.w3.org/2000/svg'} if root.tag.startswith('{'): ns['svg'] = root.tag.split('}')[0][1:] # 获取原viewBox及尺寸基准 original_viewbox = root.get('viewBox') if original_viewbox: vb_x, vb_y, vb_w, vb_h = map(float, original_viewbox.split()) else: # 无viewBox时,用宽高作为基准(默认去除px单位) vb_w = float(root.get('width', '512').replace('px', '')) vb_h = float(root.get('height', '512').replace('px', '')) vb_x, vb_y = 0, 0 # 计算缩放比例 scale_x = TARGET_WIDTH / vb_w scale_y = TARGET_HEIGHT / vb_h # 需要调整的坐标/尺寸属性列表 transform_attrs = [ 'x', 'y', 'cx', 'cy', 'r', 'rx', 'ry', 'width', 'height', 'x1', 'y1', 'x2', 'y2' ] # 遍历所有元素调整属性 for elem in root.findall('.//*', ns): for attr in transform_attrs: if attr not in elem.attrib: continue try: # 处理带单位或纯数值的属性 val_str = elem.get(attr).replace('px', '') value = float(val_str) # 根据属性类型应用对应轴的缩放比例 if attr in ['width', 'x', 'cx', 'x1', 'x2']: elem.set(attr, str(round(value * scale_x, 4))) else: elem.set(attr, str(round(value * scale_y, 4))) except (ValueError, TypeError): # 无法解析的属性直接跳过 pass # 更新根元素参数 root.set('width', str(TARGET_WIDTH)) root.set('height', str(TARGET_HEIGHT)) root.set('viewBox', TARGET_VIEWBOX) # 美化XML输出格式 rough_string = ET.tostring(root, 'utf-8') reparsed = minidom.parseString(rough_string) pretty_xml = reparsed.toprettyxml(indent=" ") # 写入输出文件 with open(output_path, 'w', encoding='utf-8') as f: f.write(pretty_xml) # 批量处理目录下所有SVG文件 directory = os.path.dirname(os.path.realpath(__file__)) for filename in os.listdir(directory): if filename.endswith('.svg') and not filename.startswith('resized_'): input_path = os.path.join(directory, filename) output_path = os.path.join(directory, f'resized_{filename}') process_svg(input_path, output_path) print(f"已处理:{filename} -> {output_path}")
方案说明
- 原生XML操作:利用Python内置库解析修改SVG,无需依赖第三方工具,跨平台兼容性强。
- 自适应缩放计算:优先读取原SVG的viewBox作为缩放基准,无viewBox时自动以宽高为基准计算比例。
- 全元素属性调整:遍历所有SVG元素,批量调整坐标、尺寸类属性,确保矢量内容同步缩放。
- 格式美化:输出前优化XML格式,避免代码混乱,保证可读性。
内容的提问来源于stack exchange,提问作者Oleksandr Myronchuk
相关产品推荐
相关产品推荐

