Konvertieren Sie SVG in PNG in Python

99

Wie konvertiere ich ein svgin pngin Python? Ich speichere das svgin einer Instanz von StringIO. Soll ich die pyCairo-Bibliothek verwenden? Wie schreibe ich diesen Code?

ram1
quelle
3
Möglicherweise ein Duplikat von stackoverflow.com/questions/2932408/…
Optimal Cynic
2
Dieser Thread hat das Problem ungelöst gelassen. Die akzeptierte Antwort kam von dem Fragesteller, der seinen fehlgeschlagenen Codeversuch teilte. Die andere Antwort schlug ImageMagick vor, aber ein Kommentator sagte, ImageMagick mache "einen schrecklichen Job bei der Interpretation von SVG". Ich möchte nicht, dass meine PNGs schrecklich aussehen, also stelle ich die Frage erneut.
Ram1
1
Versuchen Sie cairographics.org/cookbook/librsvgpython
Optimal Cynic
Die Beispiele in diesem Link sind spezifisch für Win32. Ich verwende Linux.
Ram1
Schauen Sie sich diesen Blog-Beitrag an, es sieht so aus, als ob er genau das ist, was Sie brauchen.
Giodamelio

Antworten:

58

Die Antwort lautet " pyrsvg " - eine Python-Bindung für librsvg .

Es gibt ein Ubuntu python-rsvg-Paket, das es bereitstellt. Das Durchsuchen von Google nach seinem Namen ist schlecht, da der Quellcode im GIT-Repository des Gnome-Projekts "gnome-python-desktop" enthalten zu sein scheint.

Ich habe eine minimalistische "Hallo Welt" erstellt, die SVG auf eine Kairo-Oberfläche rendert und auf die Festplatte schreibt:

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 : Ab 2014 ist das erforderliche Paket für die Fedora Linux-Distribution : gnome-python2-rsvg. Die obige Snippet-Liste funktioniert weiterhin wie sie ist.

jsbueno
quelle
1
Großartig, funktioniert gut. Aber gibt es eine Möglichkeit, cairodie Höhe und Breite des Bildes selbst bestimmen zu lassen ? Ich habe in die *.svgDatei geschaut , um die HÖHE und BREITE von dort zu extrahieren, aber beide sind auf eingestellt 100%. Natürlich kann ich mir die Eigenschaften des Bildes ansehen, aber da dies nur ein Schritt in der Bildverarbeitung ist, ist dies nicht das, was ich will.
Quapka
1
Wenn die "Breite" und "Höhe" Ihrer Dateien auf 100% eingestellt ist, gibt es keine Magie, die Cairo oder rsvg tun können, um die Größe zu erraten: Solche SVG-Dateien wurden von der Erstellungssoftware (/ person) unabhängig von der Größe belassen. Der umgebende HTML-Code zum Importieren der SVG-Datei würde die physische Größe liefern. Das "Handle" -Objekt von rsvg verfügt jedoch über eine .get_dimension_data()Methode, die für meine Beispieldatei (eine gut erzogene SVG) funktioniert - probieren Sie es aus.
Jsbueno
1
Ab 2014 können Sie für Ubuntu Folgendes verwenden: apt-get install python-rsvg
t1m0
1
Gibt es einen Schnellbefehl zum Hinzufügen eines weißen Hintergrunds zum Bild, wenn der aktuelle Hintergrund transparent ist?
Fiatjaf
@jsbueno Ich verwende Windows 8.1 und Python 2.7.11. Wie kann ich cairo und rsvg installieren und es zum Laufen bringen? Ich hatte Mühe, diese Arbeit zu machen. Übrigens +1 für Ihre ausführliche Erklärung.
Marlon Abeykoon
86

Folgendes habe ich mit cairosvg gemacht :

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

Und es funktioniert wie ein Zauber!

Weitere Informationen : cairosvg document

nemesisfixx
quelle
5
Hallo. Wissen Sie, wie ich das gleiche tun kann, ohne in eine Datei zu schreiben? Ich muss PNG-Inhalte von einem Webserver an den Browser senden, damit der Benutzer das Bild herunterladen kann. Das Speichern der PNG-Datei ist in unserem Projekt keine gültige Option. Deshalb brauche ich sie so. Danke
estemendoza
4
Ich habe das selbst gemacht. Es hängt im Wesentlichen davon ab, über welche Tools oder Frameworks Sie bei der Bearbeitung Ihrer Webanforderungen verfügen. Unabhängig davon, um was es sich handelt, besteht die Grundidee darin, svg2pngein streamObjekt im write_toParameter aufzunehmen, und dies kann entweder Ihr HTTP-Antwortobjekt sein (welches in Die meisten Frameworks sind ein dateiähnliches Objekt oder ein anderer Stream, den Sie dann mithilfe des Content-DispositionHeaders an den Browser senden. siehe hier: stackoverflow.com/questions/1012437/…
nemesisfixx
4
Für diejenigen, die Probleme mit diesem Code haben, wie ich es war: 1). bytestringakzeptiert Bytes, also konvertiere den String zuerst mit bytestring=bytes(svg,'UTF-8') 2). open('output.png','wb')
Dateimodus
3
cairosvg unterstützt nur Python 3.4+. Sie haben Python 2-Unterstützung eingestellt
Marlon Abeykoon
1
Es gab kein svg2pngfür mich, ich musste es benutzen cairosvg.surface.PNGSurface.convert(svg_str, write_to='output.png').
tobltobs
39

Installieren Sie Inkscape und rufen Sie es als Befehlszeile auf:

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

Sie können einen bestimmten rechteckigen Bereich auch nur mit Parametern fangen -j , z. B. mit der Koordinate "0: 125: 451: 217".

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

Wenn Sie nur ein Objekt in der SVG-Datei anzeigen möchten, können Sie den Parameter -imit der Objekt-ID angeben, die Sie in der SVG eingerichtet haben. Es verbirgt alles andere.

${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
blj
quelle
2
+1, da dies auch für Shell-Skripte äußerst praktisch ist. Vollständige Dokumente in der Befehlszeile von Inkscape finden Sie unter inkscape.org/doc/inkscape-man.html .
Prime
Danke, das war der einfachste Weg, dies zu tun. Unter Windows können Sie den Pfad in Umgebungsvariablen zu Ihrem Pfad hinzufügen , damit Sie nicht jedes Mal den vollständigen Pfad zu Inkscape eingeben müssen .
Alex S
3
Dies ist keine Antwort. Es ist eine Problemumgehung. OP fragte nach einer Python-Lösung.
Lamino
29

Ich verwende Wand-py (eine Implementierung des Wand-Wrappers um ImageMagick), um einige ziemlich fortgeschrittene SVGs zu importieren, und habe bisher großartige Ergebnisse erzielt! Dies ist der gesamte Code, den es braucht:

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

Ich habe das heute erst entdeckt und hatte das Gefühl, dass es sich lohnt, es für alle anderen zu teilen, die sich über diese Antwort hinwegsetzen könnten, da die meisten dieser Fragen schon eine Weile beantwortet wurden.

HINWEIS: Technisch gesehen habe ich beim Testen festgestellt, dass Sie nicht einmal den Formatparameter für ImageMagick übergeben müssen with wand.image.Image( blob=svg_file.read() ) as image: war alles, was wirklich benötigt wurde.

BEARBEITEN: Nach einem Bearbeitungsversuch von qris finden Sie hier einen hilfreichen Code, mit dem Sie ImageMagick mit einer SVG-Datei mit transparentem Hintergrund verwenden können:

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)
Streetlogics
quelle
2
Wand hat für meine PNGs viel besser funktioniert als Kairo.
Qris
3
Ich bekomme den Fehler image.read(blob=svg_file.read(), format="svg") NameError: name 'svg_file' is not defined
adrienlucca.wordpress.com
2
svg_fileIn diesem Beispiel wird angenommen, dass es sich um ein "Datei" -Objekt handelt. Die Einstellung svg_filewürde svg_file = File.open(file_name, "r")
ungefähr
2
Vielen Dank, die cairound rsvg"akzeptierte" Methode hat bei meinem PDF nicht funktioniert. pip install wandund dein Snippet hat es geschafft;)
nmz787
5
Wenn Sie ein SVG haben, müssen strSie zuerst wie folgt in eine Binärdatei codieren : svg_blob = svg_str.encode('utf-8'). Jetzt können Sie die oben beschriebene Methode verwenden , durch das Ersetzen blob=svg_file.read()mit blob=svg_blob.
Ascherbar
12

Versuchen Sie dies: http://cairosvg.org/

Die Seite sagt:

CairoSVG ist in reinem Python geschrieben und hängt nur von Pycairo ab. Es ist bekannt, dass es mit Python 2.6 und 2.7 funktioniert.

Update 25. November 2016 :

2.0.0 ist eine neue Hauptversion, deren Änderungsprotokoll Folgendes enthält:

  • Löschen Sie die Python 2-Unterstützung
user732592
quelle
Es gibt leider zwei Probleme damit. Erstens geht es nicht darum <clipPath><rect ... /></clipPath>. Zweitens wird die Option -d (DPI) nicht verwendet.
Ray
2
@ Ray, bitte senden Sie Fehlerberichte / Feature-Anfragen an den CairoSVG-Tracker !
Simon Sapin
@ Simon, kannst du es bitte tun? Ich bin zu beschäftigt und werde in den nächsten 1-2 Monaten sein.
Ray
2
@ Ray, eigentlich gibt es die Option -d / --dpi schon eine Weile, und mir wurde gesagt, dass die Unterstützung für <clippath> vor einigen Wochen in der Git-Version hinzugefügt wurde.
Simon Sapin
@ Simon, schön! Danke dir! :)
Ray
6

Eine andere Lösung, die ich gerade hier gefunden habe Wie rendere ich eine skalierte SVG in ein 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 kann einfach von einem Binärpaket in Windows installiert werden (und ich verwende es für andere Dinge, daher ist es für mich einfach).

Beim Konvertieren von Länderflaggen aus Wikimedia sind mir jedoch einige Probleme aufgefallen, daher möglicherweise nicht der robusteste SVG-Parser / Renderer.

Adam Kerz
quelle
5

Eine kleine Erweiterung der Antwort von 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)
Martin Thoma
quelle
Ich habe die SVG-Extraktion für Breite und Höhe verwendet. Ich bin mir über den SVG-Standard nicht sicher, aber in einigen meiner SVG-Dateien folgte auf die Breite oder Höhe eine nicht numerische Zeichenfolge wie 'mm' oder 'px' (z. B. '250 mm'). Das int ('250mm') löst eine Ausnahme aus und ich musste einige zusätzliche Verbesserungen vornehmen.
WigglyWorld
3

Ich fand keine der Antworten zufriedenstellend. Alle erwähnten Bibliotheken haben das eine oder andere Problem, wie Kairo, das die Unterstützung für Python 3.6 aufgibt (sie haben die Unterstützung von Python 2 vor etwa 3 Jahren eingestellt!). Auch die Installation der genannten Bibliotheken auf dem Mac war mühsam.

Schließlich fand ich die beste Lösung war svglib + reportlab . Beide wurden ohne Probleme mit pip installiert und der erste Aufruf zur Konvertierung von svg nach png funktionierte wunderbar! Sehr zufrieden mit der Lösung.

Nur 2 Befehle reichen aus:

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

Gibt es irgendwelche Einschränkungen bei diesen, die ich beachten sollte?

Sarang
quelle
Ja, das
Markerende
2

SVG-Skalierung und PNG-Rendering

Mit pycairo und librsvg ich die SVG-Skalierung und das Rendern einer Bitmap erreichen. Angenommen, Ihr SVG hat nicht genau 256 x 256 Pixel, die gewünschte Ausgabe, können Sie das SVG mit rsvg in einen Kairoer Kontext einlesen, es dann skalieren und in ein PNG schreiben.

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-Bindung

Von der Cario-Website mit einigen geringfügigen Änderungen. Auch ein gutes Beispiel für das Aufrufen einer C-Bibliothek aus 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)
Cameron Lowell Palmer
quelle
Vielen Dank dafür, es hat sich in einem meiner Projekte als sehr nützlich erwiesen. Obwohl Handle.get_dimension_dataes bei mir nicht funktioniert hat. Ich musste es durch ein einfaches Abrufen von self.props.widthund ersetzen self.props.height. Ich habe zuerst versucht, die RsvgDimensionDataStruktur wie auf der cairo-Website beschrieben zu definieren , aber ohne Erfolg.
JeanOlivier
Ich versuche dies in einem meiner Projekte zu verwenden. Wie erhalte ich die erforderlichen DLL-Dateien?
Andoo
0

Hier ist ein Ansatz, bei dem Inkscape von Python aufgerufen wird.

Beachten Sie, dass bestimmte Crufty-Ausgaben, die Inkscape während des normalen fehlerfreien Betriebs in die Konsole schreibt (insbesondere stderr und stdout), unterdrückt werden. Die Ausgabe wird in zwei Zeichenfolgenvariablen outund erfasst 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 '?')  )

Wenn Sie beispielsweise einen bestimmten Job auf meinem Mac OS-System outausführen, erhalten Sie Folgendes:

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

(Die Eingabe-SVG-Datei hatte eine Größe von 339 x 339 Pixel.)

Eisenkissen
quelle
Es ist kein End-to-End-Python, wenn Sie sich auf Inkscape verlassen.
Joel
1
@ Joel: Die erste Zeile wurde geändert, um Ihren Einwand zu überwinden. Aber natürlich basiert auch eine "reine" Python-Lösung auf Elementen außerhalb der Kernsprache und wird letztendlich mit Maschinensprache ausgeführt. Vielleicht gibt es also kein End-to-End-Verfahren!
Eisen - Kissen
0

Eigentlich wollte ich von nichts anderem als Python abhängig sein (Kairo, Tinte usw.). Meine Anforderungen sollten so einfach wie möglich sein, höchstens ein einfaches pip install "savior"würde ausreichen, deshalb hat keiner der oben genannten nicht ' passt nicht zu mir.

Ich bin durchgekommen (weiter als Stackoverflow in der Forschung). https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

Sieht soweit gut aus. Also teile ich es für den Fall, dass sich jemand in der gleichen Situation befindet.

Ualter Jr.
quelle