From: "P. J. McDermott" Origin: upstream, https://github.com/SimonSapin/cairocffi/tree/master/utils Subject: Add missing utils/ directory --- a/utils/cairocffi_to_pycairo.py +++ b/utils/cairocffi_to_pycairo.py @@ -0,0 +1,32 @@ +import ctypes +import cairo # pycairo +import cairocffi + +pycairo = ctypes.PyDLL(cairo._cairo.__file__) +pycairo.PycairoContext_FromContext.restype = ctypes.c_void_p +pycairo.PycairoContext_FromContext.argtypes = 3 * [ctypes.c_void_p] +ctypes.pythonapi.PyList_Append.argtypes = 2 * [ctypes.c_void_p] + + +def _UNSAFE_cairocffi_context_to_pycairo(cairocffi_context): + # Sanity check. Continuing with another type would probably segfault. + if not isinstance(cairocffi_context, cairocffi.Context): + raise TypeError('Expected a cairocffi.Context, got %r' + % cairocffi_context) + + # Create a reference for PycairoContext_FromContext to take ownership of. + cairocffi.cairo.cairo_reference(cairocffi_context._pointer) + # Casting the pointer to uintptr_t (the integer type as wide as a pointer) + # gets the context’s integer address. + # On CPython id(cairo.Context) gives the address to the Context type, + # as expected by PycairoContext_FromContext. + address = pycairo.PycairoContext_FromContext( + int(cairocffi.ffi.cast('uintptr_t', cairocffi_context._pointer)), + id(cairo.Context), + None) + assert address + # This trick uses Python’s C API + # to get a reference to a Python object from its address. + temp_list = [] + assert ctypes.pythonapi.PyList_Append(id(temp_list), address) == 0 + return temp_list[0] --- a/utils/cairo_coverage.py +++ b/utils/cairo_coverage.py @@ -0,0 +1,23 @@ +import inspect +import pycparser +import cairocffi + +ALL_THE_CODE = ''.join( + line + for module in [ + cairocffi, cairocffi.surfaces, cairocffi.patterns, + cairocffi.fonts, cairocffi.context, cairocffi.matrix] + for line in inspect.getsourcelines(module)[0]) + + +class Visitor(pycparser.c_ast.NodeVisitor): + def visit_Decl(self, node): + for _, child in node.children(): + if isinstance(child, pycparser.c_ast.FuncDecl): + if ('cairo.' + node.name) not in ALL_THE_CODE and not ( + node.name.endswith('user_data')): + print(node.name) + break + +print('cairo functions never used in cairocffi:\n') +Visitor().visit(pycparser.CParser().parse(cairocffi.constants._CAIRO_HEADERS)) --- a/utils/compare_pycairo.py +++ b/utils/compare_pycairo.py @@ -0,0 +1,24 @@ +import cairo as pycairo +import cairocffi + +# We want the real pycairo +assert pycairo is not cairocffi + + +print('Missing pycairo API:\n') + +for name in dir(pycairo): + pycairo_obj = getattr(pycairo, name) + cairocffi_obj = getattr(cairocffi, name, None) + if name.startswith(('_', 'version', 'CAPI')): + continue + if cairocffi_obj is None: + print(name) + elif isinstance(pycairo_obj, type): + for method_name in dir(pycairo_obj): + if method_name.startswith('__'): + continue + pycairo_method = getattr(pycairo_obj, method_name) + cairocffi_method = getattr(cairocffi_obj, method_name, None) + if cairocffi_method is None: + print('%s.%s' % (name, method_name)) --- a/utils/pango_example.py +++ b/utils/pango_example.py @@ -0,0 +1,56 @@ +# coding: utf8 +import cairocffi +import cffi + + +ffi = cffi.FFI() +ffi.include(cairocffi.ffi) +ffi.cdef(''' + /* GLib */ + typedef void* gpointer; + void g_object_unref (gpointer object); + + /* Pango and PangoCairo */ + typedef ... PangoLayout; + typedef enum { + PANGO_ALIGN_LEFT, + PANGO_ALIGN_CENTER, + PANGO_ALIGN_RIGHT + } PangoAlignment; + int pango_units_from_double (double d); + PangoLayout * pango_cairo_create_layout (cairo_t *cr); + void pango_cairo_show_layout (cairo_t *cr, PangoLayout *layout); + void pango_layout_set_width (PangoLayout *layout, int width); + void pango_layout_set_alignment ( + PangoLayout *layout, PangoAlignment alignment); + void pango_layout_set_markup ( + PangoLayout *layout, const char *text, int length); +''') +gobject = ffi.dlopen('gobject-2.0') +pango = ffi.dlopen('pango-1.0') +pangocairo = ffi.dlopen('pangocairo-1.0') + +gobject_ref = lambda pointer: ffi.gc(pointer, gobject.g_object_unref) +units_from_double = pango.pango_units_from_double + + +def write_example_pdf(target): + pt_per_mm = 72 / 25.4 + width, height = 210 * pt_per_mm, 297 * pt_per_mm # A4 portrait + surface = cairocffi.PDFSurface(target, width, height) + context = cairocffi.Context(surface) + context.translate(0, 300) + context.rotate(-0.2) + + layout = gobject_ref( + pangocairo.pango_cairo_create_layout(context._pointer)) + pango.pango_layout_set_width(layout, units_from_double(width)) + pango.pango_layout_set_alignment(layout, pango.PANGO_ALIGN_CENTER) + markup = u'Hi from Παν語!' + markup = ffi.new('char[]', markup.encode('utf8')) + pango.pango_layout_set_markup(layout, markup, -1) + pangocairo.pango_cairo_show_layout(context._pointer, layout) + + +if __name__ == '__main__': + write_example_pdf(target='pango_example.pdf') --- a/utils/pycairo_to_cairocffi.py +++ b/utils/pycairo_to_cairocffi.py @@ -0,0 +1,25 @@ +import cairo # pycairo +import cairocffi + + +def _UNSAFE_pycairo_context_to_cairocffi(pycairo_context): + # Sanity check. Continuing with another type would probably segfault. + if not isinstance(pycairo_context, cairo.Context): + raise TypeError('Expected a cairo.Context, got %r' % pycairo_context) + + # On CPython, id() gives the memory address of a Python object. + # pycairo implements Context as a C struct: + # typedef struct { + # PyObject_HEAD + # cairo_t *ctx; + # PyObject *base; + # } PycairoContext; + # Still on CPython, object.__basicsize__ is the size of PyObject_HEAD, + # ie. the offset to the ctx field. + # ffi.cast() converts the integer address to a cairo_t** pointer. + # [0] dereferences that pointer, ie. read the ctx field. + # The result is a cairo_t* pointer that cairocffi can use. + return cairocffi.Context._from_pointer( + cairocffi.ffi.cast('cairo_t **', + id(pycairo_context) + object.__basicsize__)[0], + incref=True) --- a/utils/tests.py +++ b/utils/tests.py @@ -0,0 +1,27 @@ +import io +import cairo # pycairo +import cairocffi + +from pycairo_to_cairocffi import _UNSAFE_pycairo_context_to_cairocffi +from cairocffi_to_pycairo import _UNSAFE_cairocffi_context_to_pycairo +import pango_example + + +def test(): + cairocffi_context = cairocffi.Context(cairocffi.PDFSurface(None, 10, 20)) + cairocffi_context.scale(2, 3) + pycairo_context = _UNSAFE_cairocffi_context_to_pycairo(cairocffi_context) + cairocffi_context2 = _UNSAFE_pycairo_context_to_cairocffi(pycairo_context) + assert tuple(cairocffi_context.get_matrix()) == (2, 0, 0, 3, 0, 0) + assert tuple(cairocffi_context2.get_matrix()) == (2, 0, 0, 3, 0, 0) + assert tuple(pycairo_context.get_matrix()) == (2, 0, 0, 3, 0, 0) + assert cairocffi_context2._pointer == cairocffi_context._pointer + + file_obj = io.BytesIO() + # Mostly test that this runs without raising. + pango_example.write_example_pdf(file_obj) + assert file_obj.getvalue().startswith(b'%PDF') + + +if __name__ == '__main__': + test()