Як перетворити svg
на png
, у Python? Я зберігаю svg
в екземплярі StringIO
. Чи варто користуватися бібліотекою pyCairo? Як написати цей код?
Як перетворити svg
на png
, у Python? Я зберігаю svg
в екземплярі StringIO
. Чи варто користуватися бібліотекою pyCairo? Як написати цей код?
Відповіді:
Відповідь - " 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
. Наведений вище фрагмент списку все ще працює як є.
cairo
визначати ВИСОТУ і ШИРИНУ малюнка? Я заглянув у *.svg
файл, щоб витягти звідти ВИСОту і ширину, але для нього обидва встановлено значення 100%
. Звичайно, я можу вивчити властивості зображення, але оскільки це лише один крок у обробці зображень, це не те, що я хочу.
.get_dimension_data()
метод, який працював на моєму прикладі файлу (добре поводився SVG) - спробуйте.
Ось що я зробив, використовуючи 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
svg2png
бере stream
об’єкт у write_to
параметрі, і це може бути вашим об’єктом HTTP Response (який у більшість фреймворків є файлоподібним об'єктом) або якийсь інший потік, який ви подаєте в браузер за допомогою Content-Disposition
заголовка. дивіться тут: stackoverflow.com/questions/1012437 / ...
bytestring
приймає байти, тому конвертуйте рядок спочатку з bytestring=bytes(svg,'UTF-8')
2). файловий режим повинен бути двійковим, томуopen('output.png','wb')
svg2png
для мене, я повинен був використовувати cairosvg.surface.PNGSurface.convert(svg_str, write_to='output.png')
.
Встановіть 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)
image.read(blob=svg_file.read(), format="svg") NameError: name 'svg_file' is not defined
svg_file
в цьому прикладі вважається об'єктом "файл", налаштування svg_file
виглядатиме приблизно так:svg_file = File.open(file_name, "r")
cairo
і rsvg
«прийнятий» метод не працював для мого PDF. pip install wand
і ваш фрагмент зробив трюк;)
str
то вам спочатку потрібно закодувати в довічним , як це: svg_blob = svg_str.encode('utf-8')
. Тепер ви можете скористатися описаним вище методом, замінивши blob=svg_file.read()
на blob=svg_blob
.
Спробуйте це: http://cairosvg.org/
На сайті написано:
CairoSVG написаний чистим пітоном і залежить лише від Pycairo. Відомо, що він працює на Python 2.6 та 2.7.
Оновлення від 25 листопада 2016 року :
2.0.0 - нова основна версія, її журнал змін включає:
- Видаліть підтримку Python 2
<clipPath><rect ... /></clipPath>
. По-друге, він не приймає опцію -d (DPI).
Ще одне рішення, яке я щойно знайшов тут. Як перетворити масштабований 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.
Невелике розширення відповіді 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 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")
Чи є якісь обмеження щодо цих питань, про які я повинен знати?
Використання пікаїру та лібрсгг мені вдалося досягти масштабування SVG та візуалізації до растрового зображення. Припускаючи, що ваш SVG - це не зовсім 256x256 пікселів, бажаний результат, ви можете прочитати у SVG до контексту Каїру за допомогою rsvg, а потім масштабувати його та записати у PNG.
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')
З веб-сайту 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
структуру, як описано на веб-сайті в Каїрі, але безуспішно.
Ось підхід, коли 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 (Каїр, Чорнило ... та ін.) Мої вимоги мали бути якомога простішими, щонайбільше, pip install "savior"
достатньо було б простого , ось чому жоден із наведених вище не t костюм для мене.
Я пройшов через це (підучи далі, ніж Stackoverflow у дослідженні). https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/
Виглядає добре, поки що. Тож я поділяю це на випадок, якщо хтось потрапить у таку ж ситуацію.