Конвертировать SVG в PNG на Python

Как мне конвертировать svg в pngв Python? Я храню svg в случае StringIO, Должен ли я использовать библиотеку pyCairo? Как мне написать этот код?

16 ответов

Решение

Ответ " pyrsvg" - привязка Python для librsvg.

Существует пакет python-rsvg Ubuntu, обеспечивающий это. Поиск в Google по его названию плох, потому что его исходный код, похоже, содержится в GIT-репозитории проекта gnome-python-desktop.

Я сделал минималистский "привет мир", который отображает 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, Приведенный выше фрагмент кода по-прежнему работает как есть.

Вот что я сделал с помощью 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

Установите 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. Это скрывает все остальное.

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

Я использую Wand-py (реализацию обертки Wand вокруг ImageMagick), чтобы импортировать некоторые довольно продвинутые 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() ) 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)

Я не нашел удовлетворительного ответа. Все упомянутые библиотеки имеют ту или иную проблему, например, отказ Каира от поддержки python 3.6 (они отказались от поддержки Python 2 около 3 лет назад!). Кроме того, установка упомянутых библиотек на Mac была проблемой.

Наконец, я нашел лучшее решение - svglib + reportlab. Оба были установлены без проблем с использованием pip, и первый вызов для преобразования из svg в png работал прекрасно! Очень доволен решением.

Всего две команды делают свое дело:

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

Есть ли какие-то ограничения, о которых я должен знать?

Попробуйте это: http://cairosvg.org/

На сайте написано:

CairoSVG написан на чистом Python и зависит только от Pycairo. Известно, что работает на Python 2.6 и 2.7.

Обновление 25 ноября 2016 г.:

2.0.0 - это новая основная версия, в список изменений вошли:

  • Отключить поддержку Python 2

Другое решение, которое я только что нашел здесь Как отрисовать масштабированный 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-анализатор / рендерер.

Вот еще одно решение без использования rsvg(который в настоящее время недоступен для Windows). Установите cairosvg только с помощью pip install CairoSVG

svg2png.py

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

Небольшое расширение ответа 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)

Вот пример сквозного Python.

Обратите внимание, что он подавляет определенный грубый вывод, который 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 на 339 пикселей.)

SVG масштабирование и рендеринг PNG

Используя pycairo и librsvg, я смог добиться масштабирования SVG и рендеринга в растровое изображение. Предполагая, что ваш SVG не точно 256x256 пикселей, желаемый результат, вы можете прочитать в SVG в контексте Cairo, используя rsvg, а затем масштабировать его и записать в 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 с некоторыми незначительными изменениями. Также хороший пример того, как вызвать C-библиотеку из 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)

Попробуйте этот скрипт 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')

Попробуйте использовать 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])

Публикация моего кода из этого ответа StackOverflow . Это обходной путьsvglib+reportlibне поддерживает прозрачный фон и масштабирование (см. ответ @sarang и ответ @ualter-jr, а также эти проблемы Github о неработающем масштабировании и этот о прозрачности )

Это используетpyMuPDFдля рендеринга промежуточного pdf изreportlabв PNG.

Большим преимуществом является то, что ему не нужны никакие внешние библиотеки, т.к.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")

Все ответы здесь великолепны, но я думаю, что упомяну, что я сделал простую библиотеку, которая загружает файлы SVG в виде экземпляров изображения подушки, которые затем можно экспортировать. Он использует inkscape, как в ответе blj, но отображает стандартный вывод, поэтому временные файлы не создаются. В README есть некоторые основные сведения об использовании.

https://github.com/jlwoolf/подушка-svg

РЕДАКТИРОВАТЬ:
Как было предложено, вот краткое объяснение, поскольку ссылка может стать недействительной:

Библиотека использует интерфейс командной строки inkscape для преобразования изображения в png определенного размера или dpi с помощью библиотеки подпроцессов python. Установивк, inkscape перенаправляет вывод на стандартный вывод. Первые две строки отбрасываются, а оставшийся вывод передается в , преобразуя его в экземпляр изображения подушки.

      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 (полностью не проверял, но пока работает). Таким образом, вы можете преобразовать svg в png с помощью только одной установки pip (нет необходимости использовать inkscape).

Вот объяснение того, как библиотека использует skia-python:

Сначала файл svg загружается в. Оттуда вы можете получить размеры SVGDOM, используя. Затемжелаемого размера выходного изображения. Холст масштабируется, чтобы SVG соответствовал поверхности, а затем SVG визуализируется. Оттуда можно сделать снимок изображения, который затем можно передать на.

      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, а также дополнительная документация, объясняющая использование. Надеюсь, поможет!

На самом деле, я не хотел зависеть от чего-либо другого, кроме Python (Cairo, Ink... и т. Д.). Мои требования должны были быть как можно более простыми, самое большее, простыми pip install "savior" хватило бы, поэтому ни один из вышеперечисленных мне не подошел.

Я прошел через это (идя дальше, чем Stackru в исследовании).https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

Пока все выглядит хорошо. Так что я разделяю это на случай, если кто-то окажется в такой же ситуации.

Другие вопросы по тегам