You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 10:33:15