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

64

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

  • 3
    Возможно, дубликат stackoverflow.com/questions/2932408/…
  • 2
    Эта тема оставила проблему нерешенной. Принятый ответ пришел от автора вопроса, который поделился неудачной попыткой кода. Другой ответ предложил ImageMagick, но комментатор сказал, что ImageMagick «выполняет ужасную работу по интерпретации SVG». Я не хочу, чтобы мои pngs выглядели ужасно, поэтому я снова задаю вопрос.
Показать ещё 3 комментария
Теги:
svg
rendering
cairo

8 ответов

49
Лучший ответ

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

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

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

  • 0
    Отлично, хорошо работает. Но есть ли способ позволить cairo самостоятельно определять высоту и ширину изображения? Я заглянул в файл *.svg , чтобы извлечь оттуда ВЫСОТУ и ШИРИНУ, но оба значения установлены на 100% . Конечно, я могу посмотреть на свойства картинки, но так как это только один шаг в обработке изображений, это не то, что я хочу.
  • 1
    Если для «width» и «height» ваших файлов установлено значение 100%, то Cairo или rsvg не смогут определить размер: такие SVG-файлы остались не зависящими от размера программой-создателем (/ person). Окружающий HTML-код для импорта файла SVG предоставит физический размер. Однако у объекта «Handle» в rsvg есть метод .get_dimension_data() который работал для моего файла примера (SVG с хорошим поведением) - попробуйте.
Показать ещё 8 комментариев
36

Вот что я использовал 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 document

  • 3
    Привет. Знаете ли вы, как я могу сделать то же самое, но без записи в файл? Мне нужно отправить содержимое png в браузер с веб-сервера, чтобы пользователь мог загрузить изображение. Сохранение файла png не является допустимым вариантом в нашем проекте, поэтому он мне и нужен. Спасибо
  • 3
    Я делал это сам. Это в основном зависит от того, какие инструменты или структуры у вас под рукой при обработке ваших веб-запросов, но независимо от того, что это такое, основная идея заключается в том, что svg2png принимает объект stream в параметре write_to , и это может быть как ваш объект HTTP Response. (который в большинстве фреймворков является объектоподобным объектом) или какой-то другой поток, который вы затем передаете в браузер с помощью заголовка Content-Disposition . смотрите здесь: stackoverflow.com/questions/1012437/…
Показать ещё 7 комментариев
32

Установите 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}
  • 2
    +1, потому что это также очень удобно для сценариев оболочки. Смотрите inkscape.org/doc/inkscape-man.html для полной документации по командной строке Inkscape.
  • 0
    Спасибо, это был самый простой способ сделать это. В Windows, чтобы вам не приходилось каждый раз вводить полный путь к Inkscape, вы можете добавить его в свой путь в переменных среды .
Показать ещё 1 комментарий
18

Я использую 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)
  • 1
    вау это действительно самое простое решение. Хороший =)
  • 2
    Палочка работала намного лучше чем Каир для моих PNG.
Показать ещё 6 комментариев
12

Попробуйте следующее: http://cairosvg.org/

Сайт говорит:

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

Обновить 25 ноября 2016 г.:

2.0.0 - новая крупная версия, в ее журнал изменений:

  • Поддержка Drop Python 2
  • 0
    К сожалению, с этим есть две проблемы. Во-первых, он не обрабатывает <clipPath><rect ... /></clipPath> . Во-вторых, он не принимает опцию -d (DPI).
  • 2
    @Ray, пожалуйста, отправляйте сообщения об ошибках / запросы функций на трекер CairoSVG !
Показать ещё 4 комментария
4

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

3

Небольшое расширение ответа 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)
  • 0
    Я использовал svg ширина и высота извлечения. Я не уверен насчет стандарта svg, но в некоторых моих файлах svg ширина или высота сопровождалась нечисловой строкой, такой как «mm» или «px» (например, «250mm»). Int ('250mm') выдает исключение, и мне пришлось сделать несколько дополнительных настроек.
0

Вот пример конца 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 пикселей.)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню