在 Python 中将 SVG 转换为 PNG

发布于 2024-11-18 18:05:49 字数 137 浏览 4 评论 0原文

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

How do I convert an svg to png, in Python? I am storing the svg in an instance of StringIO. Should I use the pyCairo library? How do I write that code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

罪#恶を代价 2024-11-25 18:05:49

这是我使用 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 文档

Here is what I did using 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')

And it works like a charm!

See more: cairosvg document

倥絔 2024-11-25 18:05:49

答案是“pyrsvg” - 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。上面的代码片段列表仍然按原样工作。

The answer is "pyrsvg" - a Python binding for librsvg.

There is an Ubuntu python-rsvg package providing it. Searching Google for its name is poor because its source code seems to be contained inside the "gnome-python-desktop" Gnome project GIT repository.

I made a minimalist "hello world" that renders SVG to a cairo
surface and writes it to disk:

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")

Update: as of 2014 the needed package for Fedora Linux distribution is: gnome-python2-rsvg. The above snippet listing still works as-is.

桃酥萝莉 2024-11-25 18:05:49

安装 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 文件中,您可以使用您在 SVG 中设置的对象 ID 来指定参数 -i 。它隐藏了其他一切。

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

Install Inkscape and call it as command line:

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

You can also snap specific rectangular area only using parameter -j, e.g. co-ordinate "0:125:451:217"

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

If you want to show only one object in the SVG file, you can specify the parameter -i with the object id that you have setup in the SVG. It hides everything else.

${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
南…巷孤猫 2024-11-25 18:05:49

我正在使用 Wand-py (围绕 ImageMagick 的 Wand 包装器的实现)导入一些相当高级的 SVG,到目前为止看到了很好的结果!这就是所需的全部代码:

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

我今天刚刚发现了这一点,并且觉得对于可能陷入这个答案的其他人来说值得分享,因为大多数问题得到解答已经有一段时间了。

注意:从技术上讲,在测试中我发现您实际上甚至不必传递 ImageMagick 的格式参数,因此 with wand.image.Image( blob=svg_file.read() ) 作为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)

I'm using Wand-py (an implementation of the Wand wrapper around ImageMagick) to import some pretty advanced SVGs and so far have seen great results! This is all the code it takes:

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

I just discovered this today, and felt like it was worth sharing for anyone else who might straggle across this answer as it's been a while since most of these questions were answered.

NOTE: Technically in testing I discovered you don't even actually have to pass in the format parameter for ImageMagick, so with wand.image.Image( blob=svg_file.read() ) as image: was all that was really needed.

EDIT: From an attempted edit by qris, here's some helpful code that lets you use ImageMagick with an SVG that has a transparent background:

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)
黑寡妇 2024-11-25 18:05:49

我没有发现任何令人满意的答案。所有提到的库都有一些问题或其他问题,比如 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")

我应该注意这些命令是否有任何限制?

I did not find any of the answers satisfactory. All the mentioned libraries have some problem or the other like Cairo dropping support for python 3.6 (they dropped Python 2 support some 3 years ago!). Also, installing the mentioned libraries on the Mac was a pain.

Finally, I found the best solution was svglib + reportlab. Both installed without a hitch using pip and first call to convert from svg to png worked beautifully! Very happy with the solution.

Just 2 commands do the trick:

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

Are there any limitations with these I should be aware of?

乞讨 2024-11-25 18:05:49

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

该网站说:

CairoSVG是用纯Python编写的,仅依赖于Pycairo。这是
已知可在 Python 2.6 和 2.7 上运行。

更新 2016 年 11 月 25 日

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

  • 放弃 Python 2 支持

Try this: http://cairosvg.org/

The site says:

CairoSVG is written in pure python and only depends on Pycairo. It is
known to work on Python 2.6 and 2.7.

Update November 25, 2016:

2.0.0 is a new major version, its changelog includes:

  • Drop Python 2 support
み青杉依旧 2024-11-25 18:05:49

我刚刚在这里找到的另一个解决方案 如何渲染将 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 中的二进制包安装(我将它用于其他用途,所以对我来说很容易)。

然而,我在从维基媒体转换国家标志时注意到一些问题,所以可能不是最强大的 svg 解析器/渲染器。

Another solution I've just found here How to render a scaled SVG to a 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 is easily installed from a binary package in Windows (and I use it for other things so is easy for me).

However, I noticed a few problems when converting country flags from Wikimedia, so perhaps not the most robust svg parser/renderer.

生活了然无味 2024-11-25 18:05:49

这是另一种不使用 rsvg 的解决方案(目前不适用于 Windows)。仅使用 pip install CairoSVG

svg2png.py安装 cairosvg

from cairosvg import svg2png
svg_code = open("input.svg", 'rt').read()
svg2png(bytestring=svg_code,write_to='output.png')

Here is a another solution without using rsvg(which is currently not available for windows).Only install cairosvg using pip install CairoSVG

svg2png.py

from cairosvg import svg2png
svg_code = open("input.svg", 'rt').read()
svg2png(bytestring=svg_code,write_to='output.png')
小女人ら 2024-11-25 18:05:49

对 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)

A little extension on the answer of 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)
岁月静好 2024-11-25 18:05:49

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

请注意,它会抑制 Inkscape 在正常无错误操作期间写入控制台的某些错误输出(特别是 stderr 和 stdout)。输出被捕获在两个字符串变量中:outerr

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 像素。)

Here is an approach where Inkscape is called by Python.

Note that it suppresses certain crufty output that Inkscape writes to the console (specifically, stderr and stdout) during normal error-free operation. The output is captured in two string variables, out and 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 '?')  )

For example, when running a particular job on my Mac OS system, out ended up being:

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

(The input svg file had a size of 339 by 339 pixels.)

柳絮泡泡 2024-11-25 18:05:49

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)

SVG scaling and PNG rendering

Using pycairo and librsvg I was able to achieve SVG scaling and rendering to a bitmap. Assuming your SVG is not exactly 256x256 pixels, the desired output, you can read in the SVG to a Cairo context using rsvg and then scale it and write to a 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 binding

From the Cario website with some minor modification. Also a good example of how to call a C-library from Python

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)
南烟 2024-11-25 18:05:49

尝试这个 python 脚本:

不要忘记安装 cairosvg:pip3 install cairosvg

#!/usr/bin/env python3
import os
import cairosvg

for file in os.listdir('.'):
    if os.path.isfile(file) and file.endswith(".svg"):
        name = file.split('.svg')[0]
        cairosvg.svg2png(url=name+'.svg',write_to=name+'.png')

Try this python script:

Don't forget to install cairosvg: pip3 install cairosvg

#!/usr/bin/env python3
import os
import cairosvg

for file in os.listdir('.'):
    if os.path.isfile(file) and file.endswith(".svg"):
        name = file.split('.svg')[0]
        cairosvg.svg2png(url=name+'.svg',write_to=name+'.png')

烈酒灼喉 2024-11-25 18:05:49

这个 StackOverflow 答案发布我的代码。这是 svglib+reportlib 不支持透明背景且不缩放的解决方法(请参阅@sarang 的回答@ualter-jr 的回答回答以及关于缩放不起作用这个关于透明度的

这使用 pyMuPDF 来渲染从 reportlab 到 PNG 的中间 pdf。

最大的优势是它不需要任何外部库,因为pymupdf附带了适用于Windows、Linux和MacOS的预编译轮子。

整个事情就像

pip install pymupdf svglib

然后执行以下几行一样简单

import fitz
from svglib import svglib
from reportlab.graphics import renderPDF

# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)

# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")

Posting my code from this StackOverflow answer. It's a workaround to svglib+reportlib not supporting a transparent background and no scaling (see @sarang's answer and @ualter-jr's answer as well as these Github issues on scaling not working and this one on transparency)

This uses pyMuPDF to render an intermediate pdf from reportlab to PNG.

The big advantage is that it doesn't need any external libraries as pymupdf comes with precompiled wheels for Windows, Linux and MacOS.

The whole thing is as easy as

pip install pymupdf svglib

and then executing the following lines

import fitz
from svglib import svglib
from reportlab.graphics import renderPDF

# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)

# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")
心碎的声音 2024-11-25 18:05:49

尝试使用 Gtk.Image 和 Gdk.Pixbuf

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')

from gi.repository import Gdk, Gtk
from PIL import Image

image = Gtk.Image()
image.set_from_file("path/to/image.svg")
pb = image.get_pixbuf()
pb.savev("path/to/convented/image.jpeg","jpeg",[],[])
im = Image.open("path/to/convented/image.jpeg")
pix = im.load()
print(pix[1,1])

Try using Gtk.Image and Gdk.Pixbuf

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')

from gi.repository import Gdk, Gtk
from PIL import Image

image = Gtk.Image()
image.set_from_file("path/to/image.svg")
pb = image.get_pixbuf()
pb.savev("path/to/convented/image.jpeg","jpeg",[],[])
im = Image.open("path/to/convented/image.jpeg")
pix = im.load()
print(pix[1,1])
玉环 2024-11-25 18:05:49

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

我经历了这个(比 Stackoverflow 的研究更进一步)。
https:// /www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

到目前为止看起来不错。所以我分享一下,以防有人遇到同样的情况。

Actually, I did not want to be dependent of anything else but Python (Cairo, Ink.., etc.)
My requirements were to be as simple as possible, at most, a simple pip install "savior" would suffice, that's why any of those above didn't suit for me.

I came through this (going further than Stackoverflow on the research).
https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

Looks good, so far. So I share it in case anyone in the same situation.

迷你仙 2024-11-25 18:05:49

这里的所有答案都很好,但我想我会提到我已经制作了一个简单的库,它将 SVG 文件加载为枕头图像实例,然后可以将其导出。它像 blj 的答案一样使用 inkscape,但渲染到标准输出,以便不创建临时文件。 README 中有一些基本的使用内容。

https://github.com/jlwoolf/pillow-svg

编辑:
正如所建议的,这里有一个简短的解释,因为链接可能会变得无效:

该库使用 inkscape 的命令行界面,使用 python 子进程库将图像转换为特定大小或 dpi 的 png。通过将 --export-filename 设置为 -,inkscape 将输出重定向到 stdout。前两行被丢弃,剩余的输出被传递到 PIL.Image.open ,将其转换为枕头图像实例。

import subprocess
from PIL import Image

options = ["inkscape", "--export-filename=-", "--export-type=png", "file.svg"]

pipe = subprocess.Popen(options, stdout=subprocess.PIPE)

pipe.stdout.readline()
pipe.stdout.readline()

img = Image.open(pipe.stdout)

从那里您可以执行所需的任何枕头图像操作(例如导出为 jpg、调整大小、裁剪等)。

编辑2:
刚刚添加了对 skia-python 的支持(尚未完全测试,但似乎有效迄今为止)。这样,您只需安装一次 pip 即可将 svg 转换为 png(无需使用 inkscape)。

以下是该库如何使用skia-python 的说明:

首先,将 svg 文件加载到 skia.SVGDOM 中。从那里您可以使用 containerSize 获取 SVGDOM 的尺寸。然后制作所需图像输出尺寸的skia.Surface。缩放画布以使 svg 适合表面,然后渲染 svg。从那里,可以制作图像快照,然后将其馈送到 PIL.Image.open

import skia
from PIL import Image

skia_stream = skia.Stream.MakeFromFile("file.svg")
skia_svg = skia.SVGDOM.MakeFromStream(skia_stream)

svg_width, svg_height = skia_svg.containerSize()
surface_width, surface_height = 512, 512

surface = skia.Surface(surface_width, surface_height)
with surface as canvas:
    canvas.scale(surface_width / svg_width, surface_height / svg_height)
    skia_svg.render(canvas)

with io.BytesIO(surface.makeImageSnapshot().encodeToData()) as f:
            img = Image.open(f)
            img.load()

编辑3:
我使图书馆变得更加充实。现在有一个命令行实用程序可以轻松进行 svg 转换,还有更多解释用法的文档。希望有帮助!

All the answer's here are great, but I figure I'll mention that I have made a simple library that loads SVG's files as pillow Image instances which can then be exported. It uses inkscape like in blj's answer, but renders to stdout so that no temporary files are made. There's some basic usage stuff in the README.

https://github.com/jlwoolf/pillow-svg

EDIT:
As suggested, here's a brief explanation, since the link could become invalid:

The library uses inkscape's command line interface to convert the image to a png of a specific size or dpi using the python subprocess library. By setting --export-filename to -, inkscape redirects the output to the stdout. The first two lines are discarded, and the remaining output is passed to PIL.Image.open, converting it to pillow image instance.

import subprocess
from PIL import Image

options = ["inkscape", "--export-filename=-", "--export-type=png", "file.svg"]

pipe = subprocess.Popen(options, stdout=subprocess.PIPE)

pipe.stdout.readline()
pipe.stdout.readline()

img = Image.open(pipe.stdout)

From there you can do whatever pillow image operations you need (like export as a jpg, resize, crop, etc).

EDIT 2:
Just added support for skia-python (haven't fully tested it, but seems to work so far). This way you can convert an svg to png with only a single pip install (no need to use inkscape).

Here is an explanation of how the library uses skia-python:

First, the svg file is loaded into a skia.SVGDOM. From there you can grab the SVGDOM's dimensions, using containerSize. Then a skia.Surface of the desired image output size is made. The canvas is scaled to fit the svg to the surface, and then the svg is rendered. From there, an image snapshot can be made, which can then be fed to PIL.Image.open.

import skia
from PIL import Image

skia_stream = skia.Stream.MakeFromFile("file.svg")
skia_svg = skia.SVGDOM.MakeFromStream(skia_stream)

svg_width, svg_height = skia_svg.containerSize()
surface_width, surface_height = 512, 512

surface = skia.Surface(surface_width, surface_height)
with surface as canvas:
    canvas.scale(surface_width / svg_width, surface_height / svg_height)
    skia_svg.render(canvas)

with io.BytesIO(surface.makeImageSnapshot().encodeToData()) as f:
            img = Image.open(f)
            img.load()

Edit 3:
I have fleshed out the library much much more. There is a command line utility now for easy svg conversion, along with more documentation explaining usage. Hope it helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文