Hello World (C, Cairo)

From LiteratePrograms

Jump to: navigation, search
Other implementations: Ada | ALGOL 68 | Alice ML | Amiga E | Applescript | AspectJ | Assembly Intel x86 Linux | Assembly Intel x86 NetBSD | AWK | bash | BASIC | Batch files | C | C, Cairo | C, Xlib | Candle | Clojure | C++ | C# | Delphi | Dylan | E | Eiffel | Erlang | Forth | FORTRAN | Fortress | Go | Groovy | Haskell | Hume | IBM PC bootstrap | Inform 7 | Java | Java, Swing | JavaScript | LaTeX | Lisp | Logo | Lua | Maple | MATLAB | Mercury | OCaml/F Sharp | occam | Oz | Pascal | Perl | PHP | Pic | PIR | PLI | PostScript | Prolog | Python | Rexx | Ruby | Scala | Scheme | Seed7 | sh | Smalltalk | SQL | Standard ML | SVG | Tcl | Tcl Tk | Visual Basic | Visual Basic .NET | XSL

In this article we explain how to write "Hello World!" to a PNG, PDF or PS file, or display the text in an X window, using the Cairo graphics library. Cairo is a relatively new library for rendering two-dimensional drawings. It provides drawing operators similar to those found in the Postscript and PDF formats.

To use Cairo we must include the cairo.h file, and a backend-specific header file for each backend. We also need X11/Xlib.h so that we can create a window for the xlib backend.

<<hello.c>>=
#include<cairo.h>
#include<cairo-pdf.h>
#include<cairo-ps.h>
#include<cairo-xlib.h>
#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SIZEX 100
#define SIZEY  50
paint
x
png
pdf
ps
main

Contents

Drawing

All drawing in Cairo is done to a cairo_surface_t object. That object can represent an in-memory image, a window or anything else Cairo can draw on.

A cairo_t contains the current drawing context, i.e. colors, paths, patterns etc. We use cairo_create() to create a new cairo_t object from the provided cairo_surface_t. For more complicated drawings, we can create multiple cairo_t objects, pushing them on a stack with cairo_save() and cairo_restore(). This would allow us to temporarily set for example a new color, draw a line, and continue drawing with the previous context. In this simple example, we do not need that.

<<paint>>=
void paint(cairo_surface_t *cs)
{
	cairo_t *c;
	c=cairo_create(cs);

To set the background color, we first create a rectangular "path", the size of the drawing. Then we use cairo_set_source_rgb() to set the fill color in the current context. Finally, cairo_fill() will fill a closed path with the current color and pattern.

<<paint>>=
	cairo_rectangle(c, 0.0, 0.0, SIZEX, SIZEY);
	cairo_set_source_rgb(c, 0.0, 0.0, 0.5);
	cairo_fill(c);

To print text on a given position, we use cairo_move_to() to set the "current point" ont that position. We change the current color to yellow, and use cairo_show_text() to print the text from the "current point". Cairo includes functions to manipulate text size and type face, but in order to keep things simple, we use the defaults.

<<paint>>=
	cairo_move_to(c, 10.0, 10.0);
	cairo_set_source_rgb(c, 1.0, 1.0, 0.0);
	cairo_show_text(c, "Hello World!");

The call to cairo_show_page() is necessary for generating a page in the PS and PDF backends.

When drawing is finished, we can release the cairo_t object, using cairo_destroy().

<<paint>>=
	cairo_show_page(c);
	cairo_destroy(c);
}

Xlib backend

To create an xlib surface, we must first create a window using Xlib (see Hello World (C, Xlib) for details).

<<x>>=
void showxlib()
{
	Display *dpy;
	Window rootwin;
	Window win;
	XEvent e;
	int scr;
	cairo_surface_t *cs;
	if(!(dpy=XOpenDisplay(NULL))) {
		fprintf(stderr, "ERROR: Could not open display\n");
		exit(1);
	}
	scr=DefaultScreen(dpy);
	rootwin=RootWindow(dpy, scr);
	win=XCreateSimpleWindow(dpy, rootwin, 1, 1, SIZEX, SIZEY, 0, 
			BlackPixel(dpy, scr), BlackPixel(dpy, scr));
	XStoreName(dpy, win, "hello");
	XSelectInput(dpy, win, ExposureMask|ButtonPressMask);
	XMapWindow(dpy, win);

cairo_xlib_surface_create() takes an Xlib Visual handle, which can be a Window or a Pixmap, and creates a cairo_surface_t which is ready for drawing.

<<x>>=
	cs=cairo_xlib_surface_create(dpy, win, DefaultVisual(dpy, 0), SIZEX, SIZEY);
	while(1) {
		XNextEvent(dpy, &e);
		if(e.type==Expose && e.xexpose.count<1) {
			paint(cs);
		} else if(e.type==ButtonPress) break;
	}
	cairo_surface_destroy(cs);
	XCloseDisplay(dpy);
}

PNG backend

To draw to a PNG file, we first create an in-memory image surface with cairo_image_surface_create().

<<png>>=
void writepng(const char *fname)
{
	cairo_surface_t *cs;
	cs=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, SIZEX, SIZEY);
	paint(cs);

After we are finished drawing, cairo_surface_write_to_png() makes it easy to create the PNG file.

<<png>>=
	cairo_surface_write_to_png(cs, fname);
	cairo_surface_destroy(cs);
}

PDF backend

cairo_pdf_surface_create() creates a cairo_surface_t ready for drawing. It is important that paint() contains a call to cairo_show_page(), or else the generated PDF file will be empty.

<<pdf>>=
void writepdf(const char *fname)
{
	cairo_surface_t *cs;
	cs=cairo_pdf_surface_create(fname, SIZEX, SIZEY);
	paint(cs);
	cairo_surface_flush(cs);
	cairo_surface_destroy(cs);
}

PS backend

The PS backend is used exactly like the PDF backend, except we use cairo_ps_surface_create().

<<ps>>=
void writeps(const char *fname)
{
	cairo_surface_t *cs;
	cs=cairo_ps_surface_create(fname, SIZEX, SIZEY);
	paint(cs);
	cairo_surface_flush(cs);
	cairo_surface_destroy(cs);
}

Main program

This program will write the "Hello World!" text in to a file specified on the command line. It uses the extension part of the file name to decide which format to generate. If no file is specified, the text will be presented in an X window.

<<main>>=
int main(int argc, char *argv[])
{
	char *p;
	if(argc>1) {
		if((p=strchr(argv[1], '.'))) {
			++p;
			if(!strcmp(p, "png")) writepng(argv[1]);
			else if(!strcmp(p, "pdf")) writepdf(argv[1]);
			else if(!strcmp(p, "ps")) writeps(argv[1]);
			else writepng(argv[1]);
		} else writepng(argv[1]);
	} else showxlib();
	return 0;
}

Building

To build a Cairo program on a UNIX-like system, the preferred way is to use pkg-config to get the correct compiler and linker options. Since we also use Xlib, we must specify the path to the Xlib's include file and library file.

<<Makefile>>=
CFLAGS=-Wall -pedantic -g -I/usr/X11R6/include `pkg-config --cflags cairo`
LDFLAGS=-Wall -g `pkg-config --libs cairo` -L/usr/X11R6/lib -lX11

all: hello

hello: hello.o
	cc -o hello ${LDFLAGS} hello.o

hello.o: hello.c
	cc -o hello.o ${CFLAGS} -c hello.c
Download code
Views
Personal tools