Ошибка с Python ctypes и librsvg
Я пытаюсь обернуть базовую функцию librsvg ctypes для Python, но у меня возникает ошибка.
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, path):
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)
Ошибка происходит в этой строке: librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
Есть идеи о том, что с этим не так?
2 ответа
Проблема в том, что спецификация возвращаемого типа не определена; с помощью c_void_p
одного результата недостаточно для решения проблемы в этом случае. Вам нужно поставить
librsvg.rsvg_handle_new_from_file.restype = c_void_p
в подходящем месте. Тогда это (также) работает в OSX или в 32 битах или в 64 битах.
Но я нашел более полезным расширить базовую упаковку, чтобы обрабатывать возможные ошибки при создании дескриптора из файла. Ниже приводится базовая оболочка, которая делает это. Он также в основном повторяет основное использование стандарта. rsvg
привязок.
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)
Пример использования можно увидеть на /questions/11411451/kak-ya-mogu-prochitat-dannyie-svg-insulta-v-pycairo/11411461#11411461.
librsvg.rsvg_handle_render_cairo ожидает указателей и вместо этого получает целые числа. Не уверен во всей истории здесь, но эта модификация, по крайней мере, не является ошибкой.
Попробуй это
librsvg.rsvg_handle_render_cairo(c_void_p(self.handle), c_void_p(z.ctx))
Обратите внимание, что я обернул два параметра в c_void_p, чтобы превратить их в указатели void *. Не идеально, но, похоже, работает.