在Python中将SVG转换为PNG


99

如何转换的svgpng,在Python?我将储存svg在的实例中StringIO。我应该使用pyCairo库吗?如何编写该代码?



2
该线程使问题无法解决。接受的答案来自要求其共享失败代码尝试的询问者。另一个答案建议使用ImageMagick,但有评论者说ImageMagick做“解释SVG的可怕工作”。我不想让我的PNG看起来很恐怖,所以我重新提出了问题。
ram1


该链接中的示例特定于Win32。我正在运行linux。
ram1

看一下这篇博客文章,看起来它可能正是您所需要的。
giodamelio 2011年

Answers:


60

答案是“ pyrsvg ” -一个Python绑定的librsvg

有一个提供它的Ubuntu python-rsvg软件包。在Google上搜索其名称很不方便,因为其源代码似乎包含在“ gnome-python-desktop” Gnome项目GIT存储库中。

我制作了一个极简的“ hello world”,它将SVG渲染到开罗表面并将其写入磁盘:

import cairo
import rsvg

img = cairo.ImageSurface(cairo.FORMAT_ARGB32, 640,480)

ctx = cairo.Context(img)

## handle = rsvg.Handle(<svg filename>)
# or, for in memory SVG data:
handle= rsvg.Handle(None, str(<svg data>))

handle.render_cairo(ctx)

img.write_to_png("svg.png")

更新:从2014年开始,Fedora Linux发行版所需的软件包是:gnome-python2-rsvg。上面的摘录清单仍按原样工作。


1
太好了,效果很好。但是,是否有办法cairo自行确定图片的高度和宽度?我查看了*.svg文件,从那里提取了HEIGHT和WIDTH,但是它们都设置为100%。当然,我可以查看图片的属性,但是由于这只是图像处理中的一个步骤,所以这不是我想要的。
quapka 2014年

1
如果将文件的“宽度”和“高度”设置为100%,则没有任何开罗或rsvg可以猜测大小:此类SVG文件的大小不受创建者软件(/ person)的影响。用于导入SVG文件的周围HTML代码将提供物理尺寸。但是,rsvg的“ Handle”对象确实具有.get_dimension_data()适用于我的示例文件(行为良好的SVG)的方法-试试看。
jsbueno 2014年

1
自2014年起,对于ubuntu,您可以使用:apt-get install python-rsvg
t1m0 2014年

1
如果图像的当前背景是透明的,是否有快速命令可以向图像添加白色背景?
fiatjaf 2015年

@jsbueno我使用Windows 8.1和python 2.7.11如何安装cairo和rsvg并使之工作。我正在努力使这项工作。BTW +1为您提供详细说明。
马龙·阿比库恩

88

这是我使用cairosvg做的事情

from cairosvg import svg2png

svg_code = """
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="12" cy="12" r="10"/>
        <line x1="12" y1="8" x2="12" y2="12"/>
        <line x1="12" y1="16" x2="12" y2="16"/>
    </svg>
"""

svg2png(bytestring=svg_code,write_to='output.png')

它就像一个魅力!

查看更多:cairosvg文档


5
你好 您知道我该怎么做但不写入文件吗?我需要将png内容从Web服务器推送到浏览器,以便用户可以下载图像。保存png文件在我们的项目中不是有效的选择,这就是为什么我需要这种方式。谢谢
estemendoza

4
我一直在自己做。它基本上取决于您在处理Web请求时使用的工具或框架,但是无论它是什么,基本思想是在参数svg2png中包含一个stream对象write_to,并且该对象可以是您的HTTP Response对象(大多数框架是类似文件的对象)或其他流,然后使用Content-Disposition标头将其提供给浏览器。看到这里:stackoverflow.com/questions/1012437/…–
nemesisfixx

4
对于那些经历过该代码问题的人,就像我一样:1)。bytestring接受字节,因此首先将字符串转换为bytestring=bytes(svg,'UTF-8') 2)。文件模式应为二进制,因此open('output.png','wb')
Serj Zaharchenko 2014年

3
cairosvg仅支持Python 3.4+。他们已经跌至Python的支持2
马龙Abeykoon

1
没有svg2png适合我的东西,我不得不使用cairosvg.surface.PNGSurface.convert(svg_str, write_to='output.png')
tobltobs

39

安装Inkscape并将其作为命令行调用:

${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -e ${dest_png}

您也可以仅使用参数捕捉特定的矩形区域-j,例如,坐标“ 0:125:451:217”

${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -a ${coordinates} -e ${dest_png}

如果您只想在SVG文件中显示一个对象,则可以使用-i在SVG中已设置的对象ID 来指定参数。它隐藏了其他所有内容。

${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}

2
+1,因为这对于shell脚本编写也非常方便。有关Inkscape命令行的完整文档,请参见inkscape.org/doc/inkscape-man.html
总理

谢谢,这是我发现的最简单的方法。在Windows上,要使其不必每次都键入Inkscape的完整路径,可以将其添加到Environment Variables中的Path中
Alex S

3
这不是答案。这是一种解决方法。OP要求提供Python解决方案。
拉米诺'18 -10-29

31

我正在使用Wand-py(围绕ImageMagick的Wand包装器的实现)导入一些非常高级的SVG,到目前为止,已经看到了不错的结果!这是所需要的全部代码:

    with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
        png_image = image.make_blob("png")

我今天才发现这个问题,觉得有必要与其他人分享这个问题,因为自从回答了大多数问题以来已经有一段时间了。

注意:从技术上讲,在测试中,我发现您甚至实际上不必传递ImageMagick的format参数,因此 with wand.image.Image( blob=svg_file.read() ) as image:真正需要的是所有这些。

编辑:通过qris的尝试编辑,这是一些有用的代码,可让您将ImageMagick与具有透明背景的SVG结合使用:

from wand.api import library
import wand.color
import wand.image

with wand.image.Image() as image:
    with wand.color.Color('transparent') as background_color:
        library.MagickSetBackgroundColor(image.wand, 
                                         background_color.resource) 
    image.read(blob=svg_file.read(), format="svg")
    png_image = image.make_blob("png32")

with open(output_filename, "wb") as out:
    out.write(png_image)

2
魔杖工作了很多优于开罗我的PNG图像。
qris 2014年

3
我收到错误消息 image.read(blob=svg_file.read(), format="svg") NameError: name 'svg_file' is not defined
adrienlucca.wordpress.com

2
svg_file在此示例中,假设其为“文件”对象,则设置应svg_file类似于:svg_file = File.open(file_name, "r")
streetlogics

2
谢谢你,在cairorsvg“接受”的方法没有工作的我的PDF。pip install wand和您的
摘要

5
如果您有svg,str则首先需要像这样编码为二进制:svg_blob = svg_str.encode('utf-8')。现在,您可以将上面的方法替换blob=svg_file.read()blob=svg_blob
asherbar

12

试试这个:http : //cairosvg.org/

该网站说:

CairoSVG用纯python编写,仅取决于Pycairo。已知可以在Python 2.6和2.7上运行。

2016年11月25日更新

2.0.0是一个新的主要版本,其更改日志包括:

  • 放弃对Python 2的支持

不幸的是,这有两个问题。首先,它不处理<clipPath><rect ... /></clipPath>。其次,它不使用-d(DPI)选项。

2
@Ray,请在CairoSVG跟踪器上发送错误报告/功能请求!
西蒙·萨平

@西蒙,你能做吗?我太忙了,我会在接下来的1-2个月内工作。

2
@Ray,实际上-d / --dpi选项已经存在了一段时间,并且有人告诉我在git版本中增加了对<clippath>的支持。
西蒙·萨平

@西蒙,很好!谢谢!:)

6

我在这里找到的另一个解决方案如何将缩放的SVG渲染到QImage?

from PySide.QtSvg import *
from PySide.QtGui import *


def convertSvgToPng(svgFilepath,pngFilepath,width):
    r=QSvgRenderer(svgFilepath)
    height=r.defaultSize().height()*width/r.defaultSize().width()
    i=QImage(width,height,QImage.Format_ARGB32)
    p=QPainter(i)
    r.render(p)
    i.save(pngFilepath)
    p.end()

PySide可以从Windows中的二进制软件包轻松安装(我将其用于其他用途,因此对我来说很容易)。

但是,从Wikimedia转换国家标记时,我注意到了一些问题,因此可能不是最可靠的svg解析器/渲染器。


5

关于jsbueno答案的一些扩展:

#!/usr/bin/env python

import cairo
import rsvg
from xml.dom import minidom


def convert_svg_to_png(svg_file, output_file):
    # Get the svg files content
    with open(svg_file) as f:
        svg_data = f.read()

    # Get the width / height inside of the SVG
    doc = minidom.parse(svg_file)
    width = int([path.getAttribute('width') for path
                 in doc.getElementsByTagName('svg')][0])
    height = int([path.getAttribute('height') for path
                  in doc.getElementsByTagName('svg')][0])
    doc.unlink()

    # create the png
    img = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
    ctx = cairo.Context(img)
    handler = rsvg.Handle(None, str(svg_data))
    handler.render_cairo(ctx)
    img.write_to_png(output_file)

if __name__ == '__main__':
    from argparse import ArgumentParser

    parser = ArgumentParser()

    parser.add_argument("-f", "--file", dest="svg_file",
                        help="SVG input file", metavar="FILE")
    parser.add_argument("-o", "--output", dest="output", default="svg.png",
                        help="PNG output file", metavar="FILE")
    args = parser.parse_args()

    convert_svg_to_png(args.svg_file, args.output)

我使用了svg宽度和高度提取。我不确定svg标准,但在我的某些svg文件中,宽度或高度后跟非数字字符串,例如'mm'或'px'(例如:'250mm')。int('250mm')引发异常,我不得不进行一些其他调整。
WigglyWorld

3

我没有找到满意的答案。所有提到的库都存在某些问题,例如Cairo放弃了对python 3.6的支持(它们在3年前就放弃了Python 2的支持!)。另外,在Mac上安装上述库也是很痛苦的。

最后,我发现最好的解决方案是svglib + reportlab。两者都使用pip顺利安装并且首次调用将svg转换为png都工作得很好!非常满意的解决方案。

仅需2条命令即可完成操作:

from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
drawing = svg2rlg("my.svg")
renderPM.drawToFile(drawing, "my.png", fmt="PNG")

我应该注意这些限制吗?


是的,marker-end不被支持,也不会被支持,github.com
deeplook/

2

SVG缩放和PNG渲染

使用pycairolibrsvg,我能够实现SVG缩放并将其渲染为位图。假设您的SVG并非精确地为256x256像素(所需的输出),则可以使用rsvg将SVG读入Cairo上下文中,然后对其进行缩放并写入PNG。

main.py

import cairo
import rsvg

width = 256
height = 256

svg = rsvg.Handle('cool.svg')
unscaled_width = svg.props.width
unscaled_height = svg.props.height

svg_surface = cairo.SVGSurface(None, width, height)
svg_context = cairo.Context(svg_surface)
svg_context.save()
svg_context.scale(width/unscaled_width, height/unscaled_height)
svg.render_cairo(svg_context)
svg_context.restore()

svg_surface.write_to_png('cool.png')

RSVG C绑定

从Cario网站进行一些小的修改。也是如何从Python调用C库的一个很好的例子

from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p


class _PycairoContext(Structure):
    _fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
                ("ctx", c_void_p),
                ("base", c_void_p)]


class _RsvgProps(Structure):
    _fields_ = [("width", c_int), ("height", c_int),
                ("em", c_double), ("ex", c_double)]


class _GError(Structure):
    _fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]


def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
    if rsvg_lib_path is None:
        rsvg_lib_path = util.find_library('rsvg-2')
    if gobject_lib_path is None:
        gobject_lib_path = util.find_library('gobject-2.0')
    l = CDLL(rsvg_lib_path)
    g = CDLL(gobject_lib_path)
    g.g_type_init()

    l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
    l.rsvg_handle_new_from_file.restype = c_void_p
    l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
    l.rsvg_handle_render_cairo.restype = c_bool
    l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]

    return l


_librsvg = _load_rsvg()


class Handle(object):
    def __init__(self, path):
        lib = _librsvg
        err = POINTER(_GError)()
        self.handle = lib.rsvg_handle_new_from_file(path.encode(), byref(err))
        if self.handle is None:
            gerr = err.contents
            raise Exception(gerr.message)
        self.props = _RsvgProps()
        lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))

    def get_dimension_data(self):
        svgDim = self.RsvgDimensionData()
        _librsvg.rsvg_handle_get_dimensions(self.handle, byref(svgDim))
        return (svgDim.width, svgDim.height)

    def render_cairo(self, ctx):
        """Returns True is drawing succeeded."""
        z = _PycairoContext.from_address(id(ctx))
        return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)

感谢这一点,它在我的项目中被证明非常有用。虽然Handle.get_dimension_data对我没用。我必须用简单的self.props.widthand 取而代之self.props.height。我首先尝试RsvgDimensionData按照cairo网站上的描述定义结构,但没有成功。
JeanOlivier '18 -10-1

我正在尝试在我的项目中使用它。如何获取所需的dll文件?
Andoo

0

这是Python调用Inkscape的一种方法。

请注意,它可以抑制Inkscape在正常无错误操作期间写到控制台的特定输出(特别是stderr和stdout)。输出被捕获到两个字符串变量out和中err

import subprocess               # May want to use subprocess32 instead

cmd_list = [ '/full/path/to/inkscape', '-z', 
             '--export-png', '/path/to/output.png',
             '--export-width', 100,
             '--export-height', 100,
             '/path/to/input.svg' ]

# Invoke the command.  Divert output that normally goes to stdout or stderr.
p = subprocess.Popen( cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE )

# Below, < out > and < err > are strings or < None >, derived from stdout and stderr.
out, err = p.communicate()      # Waits for process to terminate

# Maybe do something with stdout output that is in < out >
# Maybe do something with stderr output that is in < err >

if p.returncode:
    raise Exception( 'Inkscape error: ' + (err or '?')  )

例如,在我的Mac OS系统上运行特定作业时,out最终导致:

Background RRGGBBAA: ffffff00
Area 0:0:339:339 exported to 100 x 100 pixels (72.4584 dpi)
Bitmap saved as: /path/to/output.png

(输入的svg文件的大小为339 x 339像素。)


如果您依赖Inkscape,则它不是端到端的Python。
乔尔,

1
@Joel:已修改第一行以克服您的反对意见。但是,当然,即使是“纯” Python解决方案也依赖于核心语言之外的元素,并最终与机器语言一起运行,因此也许没有端到端的东西!
铁枕

0

实际上,除了Python(Cairo,Ink ..等)之外,我不想依赖于其他任何东西。我的要求是尽可能地简单,最多只需要一个简单的pip install "savior"就够了,这就是上述任何原因都不是的原因。不适合我。

我经历了这一点(在研究上比Stackoverflow还要深入)。 https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

到目前为止看起来不错。因此,如果有人遇到相同的情况,我会分享。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.