Перетворіть SVG в PNG на Python


99

Як перетворити svgна png, у Python? Я зберігаю svgв екземплярі StringIO. Чи варто користуватися бібліотекою pyCairo? Як написати цей код?


3
Можливо, дублікат stackoverflow.com/questions/2932408/…
Optimal Cynic

2
Ця нитка залишила проблему невирішеною. Прийнята відповідь надійшла від запитувача, який ділився невдалою спробою коду. Інша відповідь запропонувала ImageMagick, але коментатор сказав, що ImageMagick виконує "жахливу роботу з інтерпретації SVG". Я не хочу, щоб мої файли здавались жахливими, тому я знову задаю питання.
ram1


Приклади цього посилання специфічні для Win32. Я запускаю Linux.
ram1

Погляньте на цю публікацію в блозі, схоже, це може бути те, що вам потрібно.
giodamelio

Відповіді:


60

Відповідь - " pyrsvg " - зв'язок Python для librsvg .

Існує пакет Ubuntu python-rsvg, що забезпечує його. Пошук 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. Наведений вище фрагмент списку все ще працює як є.


1
Чудово, чудово працює. Але чи є спосіб дозволити самостійно cairoвизначати ВИСОТУ і ШИРИНУ малюнка? Я заглянув у *.svgфайл, щоб витягти звідти ВИСОту і ширину, але для нього обидва встановлено значення 100%. Звичайно, я можу вивчити властивості зображення, але оскільки це лише один крок у обробці зображень, це не те, що я хочу.
quapka

1
Якщо "ширина" та "висота" ваших файлів встановлені на 100%, то Каїр чи rsvg не можуть вгадати розмір: такі файли SVG залишали розмір незалежним від програми-розробника (/ особи). Навколишній HTML-код для імпорту SVG-файлу забезпечить фізичний розмір. Однак у "Handle" об'єкта rsvg є .get_dimension_data()метод, який працював на моєму прикладі файлу (добре поводився SVG) - спробуйте.
jsbueno

1
станом на 2014 рік, для ubuntu ви можете використовувати: apt-get install python-rsvg
t1m0

1
Чи є швидка команда для додавання білого фону до зображення, якщо його поточний фон прозорий?
fiatjaf

@jsbueno Я використовую Windows 8.1 та python 2.7.11 Як я можу встановити cairo та rsvg та змусити його працювати. Я намагався зробити це ефективно. До речі +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 у браузер із веб-сервера, щоб користувач міг завантажувати зображення. Збереження файлу png не є допустимим варіантом у нашому проекті, тому мені це потрібно саме так. Дякую
estemendoza

4
Я робив це сам. Це в основному залежить від того, якими інструментами або рамками ви користуєтесь під час обробки ваших веб-запитів, але незалежно від того, що це таке, основна ідея полягає у тому, що 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')
Серж Захарченко

3
cairosvg підтримує лише Python 3.4+. Вони відмовилися від підтримки Python 2
Marlon 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. Він приховує все інше.

${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.
Прем’єра

Дякую, це був найпростіший спосіб, коли я це зробив. Щоб зробити так, щоб вам не потрібно було кожного разу вводити повний шлях до Inkscape, ви можете додати його до свого шляху у змінних середовища .
Alex S,

3
Це не відповідь. Це обхідний шлях. OP попросив рішення Python.
lamino

31

Я використовую 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)

2
Паличка працювала набагато краще, ніж Каїр для моїх ПНГ.
qris

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
Спасибі, cairoі rsvg«прийнятий» метод не працював для мого PDF. pip install wandі ваш фрагмент зробив трюк;)
nmz787

5
Якщо у вас є SVG , strто вам спочатку потрібно закодувати в довічним , як це: svg_blob = svg_str.encode('utf-8'). Тепер ви можете скористатися описаним вище методом, замінивши blob=svg_file.read()на blob=svg_blob.
asherbar

12

Спробуйте це: http://cairosvg.org/

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

CairoSVG написаний чистим пітоном і залежить лише від Pycairo. Відомо, що він працює на Python 2.6 та 2.7.

Оновлення від 25 листопада 2016 року :

2.0.0 - нова основна версія, її журнал змін включає:

  • Видаліть підтримку Python 2

На жаль, з цим є дві проблеми. По-перше, він не справляється з <clipPath><rect ... /></clipPath>. По-друге, він не приймає опцію -d (DPI).
Рей

2
@Ray, будь ласка, надсилай звіти про помилки / запити на функції на трекер CairoSVG !
Саймон Сапін

@Simon, ти можеш це зробити, будь ласка? Я занадто зайнятий, і буду в найближчі 1-2 місяці.
Рей

2
@Ray, насправді опція -d / --dpi існує вже деякий час, і мені кажуть, що підтримка <clippath> була додана кілька тижнів тому у версії git.
Саймон Сапін

@Simon, приємно! Дякую! :)
Рей

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 (і я використовую його для інших речей, тому мені це легко).

Однак я помітив кілька проблем при перетворенні прапорів країн з Вікімедіа, тому, можливо, не найнадійніший парсер / візуалізатор 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 ('250 мм') видає виняток, і мені довелося зробити кілька додаткових налаштувань.
WigglyWorld

3

Я не знайшов жодної відповіді задовільною. Усі згадані бібліотеки мають певну проблему, або інша, як, наприклад, у Каїрі відмовляється від підтримки 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")

Чи є якісь обмеження щодо цих питань, про які я повинен знати?


Так, маркер не підтримується і не буде, github.com/deeplook/svglib/issues/177
Rainald62,

2

Масштабування SVG та візуалізація PNG

Використання пікаїру та лібрсгг мені вдалося досягти масштабування SVG та візуалізації до растрового зображення. Припускаючи, що ваш SVG - це не зовсім 256x256 пікселів, бажаний результат, ви можете прочитати у SVG до контексту Каїру за допомогою 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)

Дякую за це, це виявилось дуже корисним у моєму проекті. Хоча Handle.get_dimension_dataне працював у мене. Мені довелося замінити його простим завантаженням self.props.widthі self.props.height. Спочатку я спробував визначити RsvgDimensionDataструктуру, як описано на веб-сайті в Каїрі, але безуспішно.
JeanOlivier

Я намагаюся використовувати це у своєму проекті. Як отримати необхідні файли dll?
Andoo

0

Ось підхід, коли Inkscape викликається 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 пікселів.)


Це не наскрізний Python, якщо ви покладаєтесь на Inkscape.
Джоель

1
@ Джоел: Перший рядок змінено, щоб подолати ваше заперечення. Але, звичайно, навіть "чисте" рішення Python спирається на елементи поза основною мовою і в кінцевому підсумку працює з машинною мовою, тому, можливо, не існує такого поняття, як наскрізне щось!
Залізна подушка

0

Насправді, я не хотів бути залежним ні від чого іншого, крім Python (Каїр, Чорнило ... та ін.) Мої вимоги мали бути якомога простішими, щонайбільше, pip install "savior"достатньо було б простого , ось чому жоден із наведених вище не t костюм для мене.

Я пройшов через це (підучи далі, ніж Stackoverflow у дослідженні). https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

Виглядає добре, поки що. Тож я поділяю це на випадок, якщо хтось потрапить у таку ж ситуацію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.