Rendering SVG with libRSVG,Python and c-types

Back to cookbook

Normally it's easier to use librsvg Python bindings from PyGTK, so you can import rsvg. When that's not an option, you may be able to bind just enough of it using the ctypes module to use it. Example for win32 follows.

# Very primitive librsvg bindings for win32
# no error checking, etc.
# but hey it works!

from ctypes import *
import cairo

l=CDLL('librsvg-2-2.dll')
g=CDLL('libgobject-2.0-0.dll')
g.g_type_init()


class PycairoContext(Structure):
    _fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
                ("ctx", c_void_p),
                ("base", c_void_p)]



class RsvgDimensionData(Structure):
    _fields_ = [("width", c_int),
                ("height", c_int),
                ("em",c_double),
                ("ex",c_double)]


svghandlecache = {}


def renderSVG(ctx,filename,x,y):

    if not filename in svghandlecache:
        error=''
        handle1 = l.rsvg_handle_new_from_file(filename,error)
        svghandlecache[filename] = handle1
    else:
        handle1 = svghandlecache[filename]
    #print error
    #print handle1

    #svgDim = RsvgDimensionData()

    #l.rsvg_handle_get_dimensions(handle1,byref(svgDim))

    #print 'SVG Dimensions: ',svgDim.width,'x',svgDim.height

    #scaleX = float(WIDTH) / float(svgDim.width)
    #scaleY = float(HEIGHT) / float(svgDim.height)
    #scaleY = scaleX
    #scaleX = 1
    #scaleY = 1

    #print scaleY

    ctx.save()
    #ctx.scale(scaleX, scaleY)
    ctx.translate(x,y)

    z = PycairoContext.from_address(id(ctx))
    #print z.ctx

    #print '>>',l.rsvg_handle_render_cairo(handle1, z.ctx)
    l.rsvg_handle_render_cairo(handle1, z.ctx)
    ctx.restore()



WIDTH, HEIGHT = 400, 400

# Setup Cairo
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
ctx = cairo.Context(surface)

#bg
bg = WebColour('#FFFFFF')
ctx.set_source_rgb(bg[0],bg[1],bg[2])
ctx.paint()

renderSVG(ctx,'ball.svg',0,0)

surface.write_to_png("test.png")

An extremely minimal wrapper for rsvg that can be used in windows as a drop-in replacement for rsvg might go as follows:

#some code to give rsvg.render_cairo(ctx) ability
#on windows.
import os
try:
    import rsvg
    WINDOWS=False
except ImportError:
    print"Warning, could not import 'rsvg'"
    if os.name == 'nt':
        print "Detected windows, creating rsvg."
        #some workarounds for windows

        from ctypes import *

        l=CDLL('librsvg-2-2.dll')
        g=CDLL('libgobject-2.0-0.dll')
        g.g_type_init()

        class rsvgHandle():
            class RsvgDimensionData(Structure):
                _fields_ = [("width", c_int),
                            ("height", c_int),
                            ("em",c_double),
                            ("ex",c_double)]

            class PycairoContext(Structure):
                _fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
                            ("ctx", c_void_p),
                            ("base", c_void_p)]

            def __init__(self, path):
                self.path = path
                error = ''
                self.handle = l.rsvg_handle_new_from_file(self.path,error)


            def get_dimension_data(self):
                svgDim = self.RsvgDimensionData()
                l.rsvg_handle_get_dimensions(self.handle,byref(svgDim))
                return (svgDim.width,svgDim.height)

            def render_cairo(self, ctx):
                ctx.save()
                z = self.PycairoContext.from_address(id(ctx))
                l.rsvg_handle_render_cairo(self.handle, z.ctx)
                ctx.restore()



        class rsvgClass():
            def Handle(self,file):
                return rsvgHandle(file)

        rsvg = rsvgClass()

This wrapper may be used to render svg's with the same syntax as rsvg. i.e.:

h = rsvg.Handle("box.svg")
s = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
ctx = cairo.Context(s)
h.render_cairo(ctx)