cairocffi: CFFI-based Python bindings for cairo

Note:cairocffi is kinda old news,
but I was asked recently about it.
This is the anwser, made public.

Cairo is a 2D vector graphics library
with support for multiple backends including
image buffers, PNG, PostScript, PDF, and SVG file output.

pycairo is a set of Python bindings for cairo
that has been around for a long time.
Unfortunately, it also seems abandonned.
I’ve sent a couple
of patches
more than a year ago and haven’t heard since.

I’ve considered taking over maintainership of pycairo or forking it
but to be honest, working on it is kind of a pain.
pycairo is a CPython extension written in C,
which means it has to manually increment and decrement reference counts of Python objects.
Failure to do so correctly means leaking memory or crashing with a segmentation fault.
Even with reference counting aside,
every little thing is tedious when interacting with CPython from C code.

Now, the only reason pycairo is written in C
is to be able to call functions from cairo, a C library.
Enter CFFI,
a Python library for calling C functions from Python code.

Writing a new set of bindings using CFFI seemed way easier1
than maintining pycairo and fixing a bunch of its issues.
Thus, cairocffi was born.
It implements the same Python API as pycairo
and so is a “drop-in” replacement.
For example, CairoSVG can use either one,
without code change other than import statements.

CFFI’s dlopen() method allows loading shared libraries dynamically.
Users can therefore get a pre-compiled cairo from somewhere and use cairocffi from source,
without a working C compiler being required (which is a pain on Windows).
And I don’t need to maintain binaries for various plateforms either.

From the users’ point of view:

cairocffi uses standard Python packaging tools, and thus can easily be installed in a virtualenv. Doing so with pycairo requires some tricks.

The same code base runs on Python 2.x and 3.x (whereas py2cairo is separate from pycairo).

It runs on PyPy (and anywhere CFFI does).

It has bindings for some cairo features that were added after the last pycairo release.
Just tell me if you need more.

staticPyObject*pycairo_set_dash(PycairoContext*o,PyObject*args){double*dashes,offset=0;intnum_dashes,i;PyObject*py_dashes;if(!PyArg_ParseTuple(args,"O|d:Context.set_dash",&py_dashes,&offset))returnNULL;py_dashes=PySequence_Fast(py_dashes,"first argument must be a sequence");if(py_dashes==NULL)returnNULL;num_dashes=PySequence_Fast_GET_SIZE(py_dashes);dashes=PyMem_Malloc(num_dashes*sizeof(double));if(dashes==NULL){Py_DECREF(py_dashes);returnPyErr_NoMemory();}for(i=0;i<num_dashes;i++){dashes[i]=PyFloat_AsDouble(PySequence_Fast_GET_ITEM(py_dashes,i));if(PyErr_Occurred()){PyMem_Free(dashes);Py_DECREF(py_dashes);returnNULL;}}cairo_set_dash(o->ctx,dashes,num_dashes,offset);PyMem_Free(dashes);Py_DECREF(py_dashes);RETURN_NULL_IF_CAIRO_CONTEXT_ERROR(o->ctx);Py_RETURN_NONE;}