PythonでSVGをPNGに変換する


99

Pythonでsvgをにどのように変換しpngますか?をsvgのインスタンスに格納していますStringIO。pyCairoライブラリを使用する必要がありますか?どうやってそのコードを書くのですか?



2
そのスレッドは問題を未解決のままにしました。受け入れられた回答は、失敗したコード試行を共有していた質問者からのものでした。他の答えはImageMagickを提案しましたが、コメント者はImageMagickが「SVGを解釈するという恐ろしい仕事」をしていると述べました。私のpngを恐ろしく見せたくないので、もう一度質問します。
ram1


そのリンクの例は、Win32に固有のものです。Linuxを実行しています。
ram1

このブログの投稿を見てください。あなたが必要としているもののようです。
giodamelio

回答:


60

答えは「あるpyrsvg」 - Pythonのために結合librsvg

それを提供するUbuntu python-rsvgパッケージがあります。Googleでその名前を検索すると、ソースコードが「gnome-python-desktop」GnomeプロジェクトのGITリポジトリ内に含まれているように見えるため、うまくいきません。

SVGをcairoサーフェスにレンダリングしてディスクに書き込む、ミニマリストの「hello world」を作成しました。

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そこからHEIGHTとWIDTHを抽出するためにファイルを調べましたが、どちらもに設定されてい100%ます。もちろん、画像のプロパティを調べることはできますが、これは画像処理の1つのステップにすぎないため、これは私が望むものではありません。
quapka 2014年

1
ファイルの「幅」と「高さ」が100%に設定されている場合、サイズを推測するためにカイロやrsvgができる魔法はありません。そのようなSVGファイルは、作成者ソフトウェア(/人)によってサイズに依存しませんでした。SVGファイルをインポートするための周囲のHTMLコードは、物理的なサイズを提供します。ただし、rsvgの「ハンドル」オブジェクトには.get_dimension_data()、サンプルファイル(正常に動作するSVG)で機能するメソッドがあります。試してみてください。
jsbueno 14年

1
2014年の時点で、ubuntuの場合は次を使用できます。apt
getinstall

1
現在の背景が透明である場合、画像に白い背景を追加するための簡単なコマンドはありますか?
fiatjaf、2015年

@jsbueno Windows 8.1とpython 2.7.11を使用しています。cairoとrsvgをインストールして機能させるにはどうすればよいですか。私はこの仕事をするのに苦労していました。ところであなたの詳細な説明のための+1。
Marlon Abeykoon 2016

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

4
私はこれを自分でやっています。基本的には、Webリクエストを処理するときに手元にあるツールやフレームワークに依存しますが、それが何であれ、基本的な考え方は、パラメーター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')
Serj Zaharchenkoは'19

3
cairosvgはPython 3.4以降のみをサポートします。彼らはPython 2のサポートを終了しました
Marlon Abeykoon 2016

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ファイルでオブジェクトを1つだけ表示する場合は、SVGで設定-iしたオブジェクトIDを使用してパラメーターを指定できます。それは他のすべてを隠します。

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

2
+1。これはシェルスクリプトにも非常に便利です。Inkscapeのコマンドラインの完全なドキュメントについては、inkscape.org / doc / inkscape-man.htmlを参照してください。
プライム

おかげで、これは私がこれを行うために見つけた最も簡単な方法でした。Windowsでは、毎回Inkscapeへの完全なパスを入力する必要がないようにするために、環境変数のパスに追加できます
Alex S

3
これは答えではありません。これは回避策です。OPはPythonソリューションを求めました。
lamino 2018年

31

私はWand-py(ImageMagickのWandラッパーの実装)を使用してかなり高度なSVGをインポートしており、これまでのところ素晴らしい結果が得られています。これがすべてのコードです:

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

今日私はこれを発見しましたが、これらの質問のほとんどが回答されてからしばらく経っていたので、この回答に悩んでいる可能性のある他の誰にとっても共有する価値があると感じました。

注:技術的にテストすると、ImageMagickのformatパラメーターを渡す必要がないこともwith wand.image.Image( blob=svg_file.read() ) as image:わかったので、 本当に必要なのはそれだけでした。

編集:qrisによって試みられた編集から、透明な背景を持つSVGでImageMagickを使用できるようにするいくつかの役立つコードを次に示します。

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
ワンドは、働いていた多くの私のPNG画像のためのより良いカイロより。
qris 2014年

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
おかげで、cairorsvg「受け入れ」メソッドは私のPDFのために仕事をしませんでした。pip install wandそしてあなたのスニペットはトリックをしました;)
nmz787 '26

5
svgがある場合は、str最初に次のようにバイナリにエンコードする必要がありますsvg_blob = svg_str.encode('utf-8')。今、あなたは交換することにより、上記の方法を使用することができますblob=svg_file.read()blob=svg_blob
asherbar 2018

12

これを試してください:http : //cairosvg.org/

サイトは言う:

CairoSVGは純粋なpythonで書かれており、Pycairoにのみ依存しています。Python 2.6および2.7で動作することが知られています。

2016年11月25日更新

2.0.0は新しいメジャーバージョンであり、その変更ログには以下が含まれます。

  • Python 2のサポートを終了

残念ながら、これには2つの問題があります。まず、これはを処理しません<clipPath><rect ... /></clipPath>。次に、-d(DPI)オプションを使用しません。
Ray

2
@ Ray、CairoSVGトラッカーでバグレポート/機能リクエストを送信してください!
Simon Sapin 2013年

@サイモン、お願いできますか?私は忙しすぎて、次の1〜2か月になります。
Ray

2
@Ray、実際には-d / --dpiオプションがしばらく前から存在していて、gitバージョンで<clippath>のサポートが数週間前に追加されたと聞いています。
Simon Sapin 2013年

@サイモン、いいね!ありがとうございました!:)
Ray

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( '250mm')は例外をスローし、追加の調整が必要でした。
WigglyWorld 2015

3

満足できる回答は見つかりませんでした。言及されたすべてのライブラリーには、何か問題があるか、またはCairoがpython 3.6のサポートを落とすなどの問題があります(それらは3年前にPython 2のサポートを落としました!)。また、前述のライブラリをMacにインストールするのも面倒でした。

最後に、最良の解決策はsvglib + reportlabであることがわかりました。両方とも問題なくpipを使用してインストールされ、svgからpngに変換する最初の呼び出しは美しく機能しました!ソリューションに非常に満足しています。

たった2つのコマンドでうまくいきます。

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

私が知っておくべきこれらの制限はありますか?


はい、marker-endはサポートされていません。github.com
deeplook /

2

SVGスケーリングとPNGレンダリング

pycairolibrsvgを使用して、SVGスケーリングとビットマップへのレンダリングを実現できました。SVGが正確に256x256ピクセルではなく、望ましい出力であると想定すると、rsvgを使用してSVGをCairoコンテキストに読み取り、それをスケーリングして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のウェブサイトから若干の変更を加えたもの。また、PythonからCライブラリを呼び出す方法の良い例

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。最初にRsvgDimensionDatacairoのWebサイトで説明されているように構造を定義しようとしましたが、成功しませんでした。
JeanOlivier

私のプロジェクトでこれを使用しようとしています。必要なDLLファイルを取得するにはどうすればよいですか?
Andoo

0

これは、InkscapeがPythonによって呼び出されるアプローチです。

通常のエラーのない操作中にInkscapeがコンソール(具体的にはstderrとstdout)に書き込む特定の不正な出力を抑制することに注意してください。出力は、2つの文字列変数、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 x 339ピクセルでした。)


Inkscapeに依存している場合、それはエンドツーエンドのPythonではありません。
ジョエル

1
@ジョエル:あなたの反対を克服するために最初の行が変更されました。しかしもちろん、「純粋な」Pythonソリューションでさえ、コア言語以外の要素に依存しており、最終的にはマシン言語で実行されるため、エンドツーエンドのようなものはおそらくないでしょう。
アイアン枕

0

実際、私はPython(Cairo、Inkなど)以外に依存したくありませんでした。私の要件はできるだけ単純にすることでした。せいぜい単純なものでpip install "savior"十分でした。そのため、上記のいずれもそうしなかったのです。 Tスーツ。

私はこれを通り抜けました(調査ではStackoverflowよりも進んでいます)。 https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/

これまでのところ、よさそうだ。だから、私は同じ状況の誰かがいる場合に備えてそれを共有します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.