summaryrefslogtreecommitdiffstats
path: root/debian/patches/add-missing-utils.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/add-missing-utils.patch')
-rw-r--r--debian/patches/add-missing-utils.patch209
1 files changed, 209 insertions, 0 deletions
diff --git a/debian/patches/add-missing-utils.patch b/debian/patches/add-missing-utils.patch
new file mode 100644
index 0000000..5772bd7
--- /dev/null
+++ b/debian/patches/add-missing-utils.patch
@@ -0,0 +1,209 @@
+From: "P. J. McDermott" <pj@pehjota.net>
+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'<span font="italic 30">Hi from Παν語!</span>'
++ 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()