Table of Contents

Introduction

As can be seen on the official Pango website, Pango is a library for rendering text, particularly used for internationalization. Pango is responsible for handling text in the Gtk+ library, though it can be used almost anywhere.

This tutorial will attempt to help the reader get started using pangocairo, the Pango backend used to render text onto a Cairo surface, in their C applications (there are Pango and pangocairo bindings for other languages such as Python, but they will not be addressed here).

In order to follow this tutorial, one will require the following:

  1. A C compiler (gcc 4.1.2 was used when developing the examples used in the tutorial).

  2. Cairo and Pango libraries and development files (some Linux distributions separate the two).

Basic rendering with Cairo and Pango

Before starting to use the libraries, they must first be set up. Cairo will be initialized first to provide the surface onto which Pango will render. You can download the complete source code here, and follow it as each portion is explained in detail below.

#include <stdio.h>
#include <pango/pangocairo.h>
pangocairo.h is the header file which contains declarations of all the functions used to interface between Cairo and Pango. You need not include the headers for each of the two libraries as you would normally, since this header includes cairo.h and pango.h.

int main(int argc, char* argv[]) {
	cairo_t *cr;
	cairo_status_t status;
	cairo_surface_t *surface;

	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 100);
	cr = cairo_create(surface);

	cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
	cairo_paint(cr);
We first need to create a Cairo surface to render onto and so, we have created a Cairo context for that. cairo_t is a structure used to hold information about a particular Cairo context. It stores information such as the surface which should be drawn to when rendering using this context, the stroke width, transformation matrix and drawing source. When rendering from Gtk+, one does not need to create the context manually, the gdk_cairo_create function provides one.

Cairo can render to many different formats. In this example, we have opted to create an image surface so that it is possible to output the result as a PNG at the end, but one could equally use cairo_pdf_surface_create and output to a PDF. Changing this one line could enable you to output the final rendering as a PDF, PostScript or SVG. If you are not familiar with Cairo functions, I recommend you take a look at the very good official Cairo tutorial.

Now the surface has been made, it can be drawn to straight away. In this example, we do not want a transparent background (which is the default) so we can fill it with a solid colour. cairo_set_source_rgb sets the current drawing colour of the context passed. Cairo uses floating point values to define a colour, one for each of the r, g and b components. These colour values can be in the range of 0 to 1.0, with 1.0 being full intensity and 0.0 being none. Finally, we call cairo_paint to fill the surface with the currently active drawing colour (which we just set).

At this point, Cairo is correctly set up and ready to use so we call our own function, rendertext(cairo_t *cr), (passing the context of Cairo to it) which will render the text on the Cairo surface. We shall now look inside this function.

void rendertext(cairo_t *cr) {
	PangoLayout *layout;
	PangoFontDescription *desc;

	cairo_translate(cr, 10, 20);
	layout = pango_cairo_create_layout(cr);
PangoLayout is an important structure in Pango, being the main text object. It not only stores the text which is going to be rendered, but also various settings to control the formatting of the text. These include the amount of indentation, the spacing between lines, width of text and the text alignment. The official Pango reference manual has an image, which is reproduced below, that demonstrates the different roles of PangoLayout. As can be seen, a layout has to be created and also the co-ordinate of where the text will appear on the final image is set to (10, 20).

A PangoLayout is created from a PangoContext, which is in turn normally created from PangoFontMap objects using the pango_cairo_font_map_create_context function. One normally obtains a suitable object using pango_cairo_font_map_get_default() which returns a pointer to the default font map. The pango_cairo_create_layout function is included for convenience which creates a Pango context and uses that. The PangoLayout is rendered using backend-specific functions.

It is important that the PangoContext used by your PangoLayout is kept up to date with the particular cairo_t being used since different cairo_t objects have different font options (determined for the most part by the type of target surface). This is not a problem when using pango_cairo_create_layout since it handles it automatically, but if you wanted to later render onto a different cairo_t, you would need to use the pango_cairo_update_context function, or call pango_cairo_create_layout again.

	pango_layout_set_text(layout, "Hello World!", -1);
	desc = pango_font_description_from_string("Sans Bold 12");
	pango_layout_set_font_description(layout, desc);
	pango_font_description_free(desc);

	cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
	pango_cairo_update_layout(cr, layout);
	pango_cairo_show_layout(cr, layout);

	g_object_unref(layout);
}
This code is fairly self explanatory and shows how simple it is to draw text with Pango. First, the layout properties are set up assigning it a string (in this case "Hello World!") and an ideal font (Sans, bold and 12pt). Notice that once the font has been passed to the layout, the description must be freed in order to prevent memory leaks.

In the second half of the code, the active colour (and so, the colour the text will be drawn in) is set to pure blue on the Cario surface (and the change is then copied into the layout when pango_cairo_update_layout is called). One of the most important functions in this section of code is pango_cairo_show_layout. This renders text using the parameters stored in a layout structure to render text onto a Cairo surface.

It is also neccessary to free the Pango layout object after use to prevent memory leaks.

Now we will return to main() to save the image and shut down Cairo cleanly.

	cairo_destroy(cr);
	status = cairo_surface_write_to_png(surface, "out.png");
	cairo_surface_destroy(surface);
}
The Cairo context is first freed, then the surface that has been rendered is written to a file (in this case a PNG named out.png) by cairo_surface_write_to_png and finally, surface can then released before program exits.

Here is the output from this example:

Cairo Tricks

Now you have learnt how to do basic rendering from Pango onto a Cairo surface, it is possible to progress onto some more sophisticated tricks, using Cairo functions to change the way the text is rendered. You can download the complete source code here.

Up until now, we have been simply telling Pango to render the text onto a Cairo surface directly. While this is very simple to do, it also prevents one from using Cairo functions to manipulate the final rendering of the text. In order to allow for this, we must stop rendering directly to the surface and instead create a path inside the Cairo context representing the shape of the text.

void rendertext(cairo_t *cr) {
	PangoLayout *layout;
	layout = pango_cairo_create_layout(cr);

	pangotext(layout);

	cairo_new_path(cr);
	cairo_move_to(cr, 0, 0);
	cairo_set_line_width(cr, 0.5);

	cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
	pango_cairo_update_layout(cr, layout);

	pango_cairo_layout_path(cr, layout);
	cairo_stroke_preserve(cr);

	g_object_unref(layout);
}
You will notice first of all that most of the Pango specific layout functions have been moved into a separate function, pangotext. This is purely to improve the readability of the code, and it sets the text and font style.

Next, we shall turn our attention to the new Cairo functions called. First of all, you will most likely want to create a new path within the Cairo context (unless you have already started creating a path and want the text to the rendered as part of that). When you first create a path, it has no initial position, so we set one as (0, 0) (this is where the text will be rendered to, and it replaces the call to cairo_translate found in the previous example). The next new function, cairo_set_line_width, is fairly self explanatory. It sets the width of the line that the text will be rendered with as a path. The default width in Cairo is 2.0, so anything larger will be thicker and anything below that will give a thinner line.

When you are ready to render the text, the pango_cairo_layout_path function works in a very similar way to pango_cairo_show_layout but as was said above, it renders the text as a Cairo path rather than directly to the surface. This means Cairo can manipulate it in several ways. A very basic example of this is shown with the cairo_stroke_preserve function, which draws a stroke along whatever the active path (in this case, the shape of the text) is. This gives you simply the outline of the text, which may be more useful in some circumstances than the solid text we were presented with previously. The Cairo function to fill between the lines and give solid text is cairo_fill.

Here is the difference between cairo_stroke_preserve (left) and cairo_fill (right):

From here on, different effects can be achived simply by using clever tricks with Cairo since we have rendered the text as a path.