开发者

Error with Python ctypes and librsvg

开发者 https://www.devze.com 2023-03-08 10:32 出处:网络
I\'m trying to wrap basic function of librsvg with ctypes for Python, but I\'m getting a segfault. C: // pycairo excerpt

I'm trying to wrap basic function of librsvg with ctypes for Python, but I'm getting a segfault.

C:

// pycairo excerpt
typedef struct {
  PyObject_HEAD
  cairo_t *ctx;
  PyObject *base; /* base object used to create context, or NULL */
} PycairoContext;

// librsvg excerpt
RsvgHandle * rsvg_handle_new_from_file (const gchar * file_name, GError ** error);
// ...
gboolean rsvg_handle_render_cairo (RsvgHandle * handle, cairo_t * cr);

Python ctypes:

from ctypes import *
from ctypes import util


librsvg = cdll.LoadLibrary('/brew/lib/librsvg-2.2.dylib')
libgobject = cdll.LoadLibrary('/brew/lib/libgobject-2.0.dylib')

libgobject.g_type_init()


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


class RSVGHandle(object):

    def __init__(self, pa开发者_如何学Cth):
        self.path = path
        self.error = ''
        self.handle = librsvg.rsvg_handle_new_from_file(self.path, self.error)

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


import cairo

h = RSVGHandle('bank.svg')
s = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
ctx = cairo.Context(s)


# segmentation fault....
h.render_cairo(ctx)

The error is happening in this line: librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)

Any idea about what's wrong with this?


The problem is that the specification of the return type is not defined; using c_void_p on the result alone isn't enough to solve the problem in that case. You need to put

librsvg.rsvg_handle_new_from_file.restype = c_void_p

in an appropriate place. Then it (also) works in OSX either in 32 bits or 64 bits.

But I've found more helpful to augment the basic wrapping in order to handle possible errors when creating a handle from a file. Following is a basic wrapper that does that. It also replicates in a mostly identical way the basic use of the standard rsvg bindings.

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

Example usage can be seen at https://stackoverflow.com/a/14928770/1832154.


librsvg.rsvg_handle_render_cairo expects pointers, and is getting integers instead. Not sure of the entire story here but this modification at least does not segfault.

Try this

 librsvg.rsvg_handle_render_cairo(c_void_p(self.handle), c_void_p(z.ctx))

Notice I wrapped the two parameters in c_void_p to make them into void * pointers. Not ideal, but it seems to work.

0

精彩评论

暂无评论...
验证码 换一张
取 消