Cairo provides functions for doing rotations and scaling about the origin: cairo_rotate() and cairo_scale() for directly affecting the transformation matrix of a context, and cairo_init_rotate()/cairo_rotate() and cairo_init_scale()/cairo_scale() for computing a transformation matrix separately from a context. But what if you want to perform rotation or scaling about a point other than the origin?

In fact, this is quite simple. Break up the transformation into 3 separate steps:

1. perform a translation that moves the specified point to the origin
2. perform the desired rotation or scaling about the origin
3. perform the inverse of the translation in step 1, moving the origin back to the specified point.

In Cairo code, these steps have to be performed in the reverse order (see here for why). Supposing we want to rotate by angle radians about the point (centre_x, centre_y). The code would look like

cairo_translate(cr, centre_x, centre_y); /* step 3 */
cairo_rotate(cr, angle); /* step 2 */
cairo_translate(cr, - centre_x, - centre_y); /* step 1 */

where the comments refer to the steps in the previous list.

Similarly, if we wanted to compute such a transformation as a separate matrix for use later, we could do it like

cairo_matrix_t mat;
cairo_matrix_init_identity(&mat);
cairo_matrix_translate(&mat, centre_x, centre_y); /* step 3 */
cairo_matrix_rotate(&mat, angle); /* step 2 */
cairo_matrix_translate(&mat, - centre_x, - centre_y); /* step 1 */

Here I could have used cairo_matrix_init_translate() to combine the first two Cairo calls into one, but I kept them separate so it is clear that the second call corresponds exactly to step 3 in the above list.