Rendering text with Pango, Cairo and Freetype
Pango is a great open source text layouting library which I've been using on several projects. The only problem is that the build process has quite some peculiarities that you need to know about. My goal was to create a build script that compiles all the dependencies for Pango. This script compiles all the necessary libraries and is tested on Mac OS 10.9. and Arch Linux
Compilation challenges
There are some things you need to know when compiling Pango on Mac. If
you want to use Freetype as a rasterization backend you need to
compile harfbuzz first, because the configure script won't use
freetype without harfbuzz. Harfbuzz has dependencies with ragel and
colm which at the time of writing couldn't be configured because the
build script tests for the colm version doesn't work on Mac. To make
ragel compile I changed its configure.ac file and commented the
check for the colm version as I was compiling colm 0.13 which is needs
(the build script does this for you). The build script also compiles
gtkdoc because some of libraries (including pango) are compiled from
the the latest git version that don't contain a ready to use configure
script. Instead they have a ./autogen.sh script that uses
autoconfig to generate these scripts and the ./autogen.sh need
gtkdocs in some cases. Note that the configure.ac of the current
pango git version doesn't work on Linux so we're using a diffrent
version there. The build script manipulates the catalog.xml for the
gtkdoc compilation. I'm also compiling pango with
--with-included-modules so the modules are linked statically. After
compiling you need to have the libpango-1.0.a and
libpangoft2-1.0.a files. When you have these you can use freetype
with pango.
Finding fonts
Pango uses FontConfig to find fonts and before FontConfig can find
fonts you need to create a fonts.conf. FontConfig uses a environment
variable called FONTCONFIG_PATH where is looks for this fonts.conf
file. For convenience purposes you can create a fonts.conf file in
the same directory as you executable with the following information
and use a shell script to set a temporary FONTCONFIG_PATH variable.
Place your font files in this ./fonts/ directory and fontconfig
will be able to find your fonts.
<?xml version="1.0"?> <!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <fontconfig> <dir>./fonts/</dir> <cachedir>./fonts/cache/</cachedir> <config></config> </fontconfig>
Make sure to set the FONTCONFIG_PATH variable before you start
the application:
#!/bin/sh export FONTCONFIG_PATH=. ./your_application
Pango, Cairo and Freetype code example
The code snippet below shows an example how you can use Pango with Cairo and Freetype font rasterization. The code has been tested on Mac 10.9 and Arch Linux and linked against the libraries that are compiled by the build script.
/* Text layouting and rendering with Pango --------------------------------------- This code snippet shows how to create a cairo surface and render some text into it using Pango. We store the generated pixels in a png file. n.b. this file was created for testing not production. */ #include <stdio.h> #include <stdlib.h> #include <cairo.h> #include <freetype2/ftbitmap.h> #include <pango/pangocairo.h> #include <pango/pangoft2.h> #define USE_FREETYPE 1 #define USE_RGBA 0 int main() { cairo_surface_t* surf = NULL; cairo_t* cr = NULL; cairo_status_t status; PangoContext* context = NULL; PangoLayout* layout = NULL; PangoFontDescription* font_desc = NULL; PangoFontMap* font_map = NULL; FT_Bitmap bmp = {0}; int stride = 0; int width = 640; int height = 480; /* ------------------------------------------------------------ */ /* I N I T I A L I Z E */ /* ------------------------------------------------------------ */ /* FT buffer */ FT_Bitmap_New(&bmp); bmp.rows = height; bmp.width = width; bmp.buffer = (unsigned char*)malloc(bmp.rows * bmp.width); if (NULL == bmp.buffer) { printf("+ error: cannot allocate the buffer for the output bitmap.\n"); exit(EXIT_FAILURE); } /* create our "canvas" */ bmp.pitch = (width + 3) & -4; bmp.pixel_mode = FT_PIXEL_MODE_GRAY; /*< Grayscale*/ bmp.num_grays = 256; stride = cairo_format_stride_for_width(CAIRO_FORMAT_A8, width); surf = cairo_image_surface_create_for_data(bmp.buffer, CAIRO_FORMAT_A8, width, height, stride); if (CAIRO_STATUS_SUCCESS != cairo_surface_status(surf)) { printf("+ error: couldn't create the surface.\n"); exit(EXIT_FAILURE); } /* create our cairo context object that tracks state. */ cr = cairo_create(surf); if (CAIRO_STATUS_NO_MEMORY == cairo_status(cr)) { printf("+ error: out of memory, cannot create cairo_t*\n"); exit(EXIT_FAILURE); } /* ------------------------------------------------------------ */ /* D R A W I N T O C A N V A S */ /* ------------------------------------------------------------ */ font_map = pango_ft2_font_map_new(); if (NULL == font_map) { printf("+ error: cannot create the pango font map.\n"); exit(EXIT_FAILURE); } context = pango_font_map_create_context(font_map); if (NULL == context) { printf("+ error: cannot create pango font context.\n"); exit(EXIT_FAILURE); } /* create layout object. */ layout = pango_layout_new(context); if (NULL == layout) { printf("+ error: cannot create the pango layout.\n"); exit(EXIT_FAILURE); } /* create the font description @todo the reference does not tell how/when to free this */ font_desc = pango_font_description_from_string("Station 35"); pango_layout_set_font_description(layout, font_desc); pango_font_map_load_font(font_map, context, font_desc); pango_font_description_free(font_desc); /* set the width around which pango will wrap */ pango_layout_set_width(layout, 150 * PANGO_SCALE); /* write using the markup feature */ const gchar* text = "" "<span foreground=\"blue\" font_family=\"Station\">" " <b> bold </b>" " <u> is </u>" " <i> nice </i>" "</span>" "<tt> hello </tt>" "<span font_family=\"sans\" font_stretch=\"ultracondensed\" letter_spacing=\"500\" font_weight=\"light\"> SANS</span>" "<span foreground=\"#FFCC00\"> colored</span>" ""; gchar* plaintext ; PangoAttrList* attr_list; pango_layout_set_markup(layout, text, -1); /* render */ pango_ft2_render_layout(&bmp, layout, 30, 100); pango_cairo_update_layout(cr, layout); /* ------------------------------------------------------------ */ /* O U T P U T A N D C L E A N U P */ /* ------------------------------------------------------------ */ /* write to png */ status = cairo_surface_write_to_png(surf, "test_font.png"); if (CAIRO_STATUS_SUCCESS != status) { printf("+ error: couldn't write to png\n"); exit(EXIT_FAILURE); } cairo_surface_destroy(surf); cairo_destroy(cr); g_object_unref(layout); g_object_unref(font_map); g_object_unref(context); return 0; }
To compile and run the above example I used the following script. I'm also
settings the FONTCONFIG_PATH in this scripts so it can find the font files.
Also, this example shows what include paths you need to set and with what libraries
you need to link (this example is created for Mac 10.9).
#!/bin/sh
is_mac=n
is_linux=n
if [ "$(uname)" == "Darwin" ]; then
is_mac=y
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
is_linux=y
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then
echo "Windows not yet supported."
exit
fi
if [ "${is_mac}" = "y" ] ; then
clang -o test_pango \
-I./install/include/cairo \
-I./install/include \
-I./install/include/freetype2/ \
-I./install/include/pango-1.0/ \
-I./install/include/glib-2.0 \
-I./install/lib/glib-2.0/include/ \
test_pango.cpp \
-L./install/lib \
-lpango-1.0 \
-lglib-2.0 \
-lgobject-2.0 \
-lgmodule-2.0 \
-lffi \
-lintl \
-lcairo \
-lpixman-1 \
-lpng \
-lfreetype \
-lpangocairo-1.0 \
-lpangoft2-1.0 \
-lharfbuzz \
-lfontconfig \
-lexpat \
-liconv \
-framework CoreFoundation \
-framework Cocoa
if [ -f ./test_pango ] ; then
export FONTCONFIG_PATH=.
./test_pango
fi
else
g++ -o test_pango \
-I./install/include/cairo \
-I./install/include \
-I./install/include/freetype2/ \
-I./install/include/pango-1.0/ \
-I./install/include/glib-2.0 \
-I./install/lib/glib-2.0/include/ \
test_pango.cpp \
-L./install/lib \
./install/lib/libpangocairo-1.0.a \
./install/lib/libpangoft2-1.0.a \
./install/lib/libpango-1.0.a \
./install/lib/libfontconfig.a \
./install/lib/libgobject-2.0.a \
./install/lib/libgmodule-2.0.a \
./install/lib/libglib-2.0.a \
./install/lib/libffi.a \
./install/lib/libcairo.a \
./install/lib/libpixman-1.a \
./install/lib/libpng12.a \
./install/lib/libfreetype.a \
./install/lib/libharfbuzz.a \
-lexpat \
-lpthread \
-ldl \
-lz \
-lbz2
if [ -f ./test_pango ] ; then
export FONTCONFIG_PATH=.
./test_pango
fi
fi
Dependencies
This post uses the following libraries in the build script. On linux we used gtk-doc from a package manager (pacman -S gtk-doc).
| Library/util | Version or git commit |
|---|---|
| autoconf | 2.69 |
| libtool | 2.4.2 |
| automake | 1.14 |
| pkgconfig | 0.28 |
| gtkdoc (only on mac) | 3576d64d4a45f5ccba1f6304a3476771f0c188ab |
| pixman | 0.32.6 |
| gettext | 0.19.2 |
| libxml2 | 2.9.1 |
| fontconfig | 2.11.1 |
| libpng | 1.2.51 |
| libjpg | v9a |
| colm | ca708c6655243d93017c4b21dd5b36e0d1dfd431 |
| ragel | 246236afbcb5b8cd53fa1f7d1e23fbb1447cebfc |
| harfbuzz | 0.9.35 |
| freetype | bc12d9e9ac7c4960ad4539a48b261193a06fad88 |
| glib | 27405ae878ebabb4361d38ed22be11b6891fd866 |
| cairo | 0aa43ed886c0f8468a21a470f2f024bd4d8a4513 |
| pango | 0d1945ed2c602e295e433ce7e9e1ecbc6600c76a |
NAT Types
Building Cabinets
Compiling GStreamer from source on Windows
Debugging CMake Issues
Dual Boot Arch Linux and Windows 10
Mindset Updated Edition, Carol S. Dweck (Book Notes)
How to setup a self-hosted Unifi NVR with Arch Linux
Blender 2.8 How to use Transparent Textures
Compiling FFmpeg with X264 on Windows 10 using MSVC
Blender 2.8 OpenGL Buffer Exporter
Blender 2.8 Baking lightmaps
Blender 2.8 Tips and Tricks
Setting up a Bluetooth Headset on Arch Linux
Compiling x264 on Windows with MSVC
C/C++ Snippets
Reading Chunks from a Buffer
Handy Bash Commands
Building a zero copy parser
Kalman Filter
Saving pixel data using libpng
Compile Apache, PHP and MySQL on Mac 10.10
Fast Pixel Transfers with Pixel Buffer Objects
High Resolution Timer function in C/C++
Rendering text with Pango, Cairo and Freetype
Fast OpenGL blur shader
Spherical Environment Mapping with OpenGL
Using OpenSSL with memory BIOs
Attributeless Vertex Shader with OpenGL
Circular Image Selector
Decoding H264 and YUV420P playback
Fast Fourier Transform
OpenGL Rim Shader
Rendering The Depth Buffer
Delaunay Triangulation
RapidXML
Git Snippets
Basic Shading With OpenGL
Open Source Libraries For Creative Coding
Bouncing particle effect
OpenGL Instanced Rendering
Mapping a texture on a disc
Download HTML page using CURL
Height Field Simulation on GPU
OpenCV
Some notes on OpenGL
Math
Gists to remember
Reverse SSH
Working Set
Consumer + Producer model with libuv
Parsing binary data
C++ file operation snippets
Importance of blur with image gradients
Real-time oil painting with openGL
x264 encoder
Generative helix with openGL
Mini test with vector field
Protractor gesture recognizer
Hair simulation
Some glitch screenshots
Working on video installation
Generative meshes
Converting video/audio using avconv
Auto start terminal app on mac
Export blender object to simple file format