cairocffi

CFFI-based Python bindings for the Cairo 2D graphics library, a drop-in Pycairo replacement with no C compiler required at install time.

Library
PyPI
v1.7.1
211stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
59/100Fair
Development Activity52
Maintenance24
Community80
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture78
Code Quality80
Innovation62
Learning Curve85

cairocffi is a CFFI-based set of Python bindings and an object-oriented API for Cairo, the 2D vector graphics library used for rendering to image buffers, PNG, PostScript, PDF, and SVG output. Rather than compiling a C extension against Cairo’s headers like Pycairo, cairocffi loads the already-installed libcairo shared library at runtime via CFFI, which removes the build-time compiler dependency that often blocks Pycairo installs in constrained environments.

Its public API is intentionally close to Pycairo’s, down to matching class and method names, so most code can switch by changing an import or calling install_as_pycairo() to make import cairo resolve to cairocffi transparently. Beyond the core drawing API (surfaces, contexts, patterns, fonts, matrices), it ships an optional cairocffi.pixbuf module for decoding raster image formats via GDK-PixBuf and an optional XCB surface for rendering directly to X11 windows via xcffib. It’s the drawing backend behind tools like WeasyPrint’s HTML-to-PDF pipeline.

What You Get

  • A near drop-in replacement for Pycairo’s API, plus an install_as_pycairo() shim to intercept import cairo for code you can’t edit
  • CFFI-based dynamic loading of the system libcairo shared library at import time, with no C compiler needed to install the package
  • Surface classes for every major Cairo output backend: ImageSurface, PDFSurface, PSSurface, SVGSurface, RecordingSurface, and Win32 variants
  • An optional cairocffi.pixbuf module that decodes JPEG, GIF, and other raster formats via GDK-PixBuf for use as Cairo image sources
  • Optional XCB surface support (via xcffib) for drawing directly to X11 windows
  • Typed Python exceptions (CairoError, MemoryError, IOError, FileNotFoundError) raised from Cairo’s C status codes instead of silent failures

Common Use Cases

  • Rendering PDF or SVG reports, invoices, and charts from a backend service without shelling out to an external rendering tool
  • Powering HTML/CSS-to-PDF typesetting pipelines (e.g. WeasyPrint) that need a low-level 2D drawing backend
  • Migrating projects off Pycairo in environments (older manylinux images, some Windows toolchains) where compiling its C extension is impractical
  • Drawing custom UI elements or vector graphics in GTK-based or X11 desktop applications via XCB surfaces

Under The Hood

Architecture cairocffi’s public API is assembled entirely in cairocffi/__init__.py (lines 146-161), which imports concrete classes from focused submodules — surfaces.py (Surface, ImageSurface, PDFSurface, PSSurface, SVGSurface, RecordingSurface, Win32Surface family), patterns.py, fonts.py, context.py (Context), and matrix.py (Matrix) — and re-exports everything from constants.py as the public namespace. The FFI boundary is isolated in ffi.py, which builds two cffi.FFI() instances: one loaded with the full C header set from constants._CAIRO_HEADERS (plus _CAIRO_XCB_HEADERS if xcffib is importable), and a second ffi_pixbuf that includes the first and adds GDK-PixBuf’s C declarations for image decoding. The shared library is opened once at import time via a dlopen() helper that tries platform-specific library names and filenames, raising a descriptive OSError with install instructions if none load — every wrapper class binds against this single load point. Each public class holds a cffi pointer plus a _check_status() call after nearly every C call, translating Cairo’s status codes into typed Python exceptions. There’s no dependency-injection layer or plugin system — it’s a direct binding layer tightly coupled to Cairo’s C ABI.

Tech Stack The only runtime dependency is cffi>=1.1.0, used purely as an FFI mechanism rather than a build-time compiler, which is the library’s core differentiator from Pycairo. Packaging uses flit_core with dynamic versioning read from cairocffi/__init__.py. Optional extras are declared per feature: xcb (xcffib>=1.4.0) for X11 surface support, doc (Sphinx) for the docs published at doc.courtbouillon.org, and test (pytest, ruff, numpy, pikepdf). CI runs the full matrix across Ubuntu, macOS, and Windows and Python 3.10 through 3.14, installing native Cairo/GDK-PixBuf binaries per platform before running the test suite, plus a dedicated ruff style-check job. There’s no database, web framework, or ORM in scope — the entire dependency surface is the Cairo and GDK-PixBuf C libraries loaded at runtime, plus reuse of the existing xcffib project for XCB rather than reimplementing it.

Code Quality The package ships a substantial in-package test suite (test_cairo.py with roughly 47 test functions, plus test_pixbuf.py, test_xcb.py, and test_numpy.py) run via pytest, with coverage configuration in pyproject.toml including pragma: no cover carve-outs for unreachable platform-specific error paths. Style is enforced with ruff as a required CI job, with an explicit lint rule selection and a couple of documented exceptions for characters in Cairo’s own naming. Error handling is deliberate: _check_status() runs after nearly every Cairo C call and raises typed exceptions rather than swallowing failures. Naming mirrors Cairo’s C API and Pycairo’s conventions closely, aiding discoverability for anyone already familiar with either. CI exercises the full OS/Python matrix on every push and pull request, which matters given how platform-sensitive dynamic library loading is. No static type checker configuration was found in the repo.

API Design cairocffi’s differentiator over Pycairo isn’t a new drawing capability but a build-time one: Pycairo requires compiling a C extension against Cairo’s headers, which is often the actual install blocker in constrained environments; cairocffi instead loads the already-installed shared library at runtime via CFFI, needing no compiler at install time. The API is intentionally near-identical to Pycairo’s, down to matching method names, and the install_as_pycairo() shim lets it stand in for the cairo module in code that can’t be edited directly. The tradeoff — zero build step in exchange for needing libcairo discoverable at runtime — is documented clearly with platform-specific guidance (LD_LIBRARY_PATH on Linux, CAIROCFFI_DLL_DIRECTORIES on Windows). This runtime-loading approach is also why a single cairocffi release can work against a wide range of installed Cairo versions rather than pinning to headers available at compile time.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search