如何用Python将含外链图片的SVG转换为PNG?
问题描述
目标是从包含外链图片的SVG生成PNG图片,流程分为两步:先生成新的SVG文件,再将其转换为PNG。
现有代码可成功为源SVG中的每个路径生成对应的新SVG文件,且新文件能正确插入原图,但转换后的PNG仅显示路径,不显示图片。由于图片体积过大(单行约1300万列),无法采用Base64嵌入的方式,求其他实现方法。
现有代码
import os from shutil import copyfile from lxml import etree os.environ['path'] += r';C:\Program Files\Inkscape' #only way I found to have cairo work import cairosvg def create_svg_from_path(svg_path, output_folder): tree = etree.parse(svg_path) root = tree.getroot() width = root.get('width') height = root.get('height') image = root.find('.//{http://www.w3.org/2000/svg}image') if image is not None: # Get image attributes image_width = image.get('width') image_height = image.get('height') image_x = image.get('x') image_y = image.get('y') image_href = image.get('{http://www.w3.org/1999/xlink}href') image_filename = os.path.basename(image_href) target_image_path = os.path.join(output_folder, image_filename) if not os.path.exists(target_image_path): # Assuming the script is in the same directory as the image file source_image_path = os.path.join(os.path.dirname(os.path.abspath(svg_path)), image_filename) copyfile(source_image_path, target_image_path) for path in root.findall('.//{http://www.w3.org/2000/svg}path'): # Get the path ID path_id = path.get('id') output_svg_filename = os.path.join(output_folder, f"{path_id}.svg") new_root = etree.Element('svg', version="1.0", width=width, height=height, xmlns="http://www.w3.org/2000/svg") new_image = etree.SubElement(new_root, 'image', attrib={ 'x': image_x, 'y': image_y, 'width': image_width, 'height': image_height, '{http://www.w3.org/1999/xlink}href': f"{image_filename}", 'preserveAspectRatio': 'none' }) group = etree.SubElement(new_root, 'g') new_path = etree.SubElement(group, 'path') new_path.attrib.update(path.attrib) with open(output_svg_filename, 'wb') as file: file.write(etree.tostring(new_root, pretty_print=True, xml_declaration=True, encoding='utf-8')) # Convert the SVG file to PNG output_png_filename = os.path.join(output_folder, f"{path_id}.png") cairosvg.svg2png(url=output_svg_filename, write_to=output_png_filename) if __name__ == "__main__": input_svg_file ="file.svg" output_folder = "output_svgs" if not os.path.exists(output_folder): os.makedirs(output_folder) create_svg_from_path(input_svg_file, output_folder)
解决方案建议
1. 修复SVG的命名空间声明
当前生成的新SVG根元素未声明xlink命名空间,导致<image>标签的xlink:href属性无法被正确解析,这是图片不显示的核心原因之一。修改创建new_root的代码,添加xlink命名空间:
new_root = etree.Element( 'svg', version="1.0", width=width, height=height, xmlns="http://www.w3.org/2000/svg", xmlns:xlink="http://www.w3.org/1999/xlink" # 添加这一行 )
同时,在创建<image>元素时,直接使用xlink:href作为属性名:
new_image = etree.SubElement(new_root, 'image', attrib={ 'x': image_x, 'y': image_y, 'width': image_width, 'height': image_height, 'xlink:href': f"{image_filename}", # 改为直接用xlink:href 'preserveAspectRatio': 'none' })
2. 确保CairoSVG能正确定位图片
CairoSVG处理相对路径时,默认以当前工作目录为基准,而非SVG文件所在目录。可以通过两种方式解决:
- 方式一:将图片的
href改为绝对路径# 替换原有的image_filename赋值为绝对路径 target_image_path = os.path.join(output_folder, image_filename) # ... 复制图片的代码不变 ... # 创建image元素时用绝对路径 'xlink:href': target_image_path, - 方式二:调用
cairosvg.svg2png时指定resource_path参数,将资源搜索路径设置为SVG文件所在目录cairosvg.svg2png( url=output_svg_filename, write_to=output_png_filename, resource_path=output_folder # 指定资源搜索目录为输出文件夹 )
3. 改用Inkscape命令行工具转换
CairoSVG对大图片或复杂SVG的兼容性不如Inkscape,既然你已经将Inkscape加入系统路径,直接用subprocess调用Inkscape命令行转换,可靠性更高:
import subprocess # 替换原有的cairosvg转换代码 output_png_filename = os.path.join(output_folder, f"{path_id}.png") subprocess.run([ 'inkscape', output_svg_filename, '--export-type=png', '--export-filename=' + output_png_filename, '--export-dpi=96' # 可根据需要调整DPI ], check=True)
内容的提问来源于stack exchange,提问作者Oran G. Utan
相关产品推荐
相关产品推荐

