 | Level: Intermediate Tony Graham (tkg@menteith.com), Senior consultant, Mulberry Technologies
01 Apr 2001 Pango is an open-source framework for the layout and rendering of internationalized text, and is being included in the next generation of GTK+ and GNOME. In the second of a two-part series, Tony Graham describes where to get Pango and the development versions of GLib and GTK+, how to configure the programs, and how to compile programs that use Pango and GTK+. He then illustrates the use of the pango_parse_markup() function described in part 1. The article concludes with two examples of how to use bidirectional text in GTK+ labels: one that uses pango_parse_markup() and one that uses the gtk_label_set_markup() function that is available in GTK+ now that it incorporates Pango. Getting Pango
Pango is available for download from www.pango.org (see Resources). At the time of
this writing, the current version is Pango 0.14. It is available as source code, as Red
Hat-style source RPM (RPMS), and as a binary RPM distribution for Red
Hat Linux 7.0. The other packages that are necessary for creating and
displaying widgets with internationalized text -- GTK+ 1.3.3 and GLib
1.3.3 -- are also available from the site. The latest development versions of Pango, GTK+, and GLib are always
available from the GNOME CVS (see Resources). Pango also depends on Dov Grobgeld's FriBidi package. You can use
the "mini" version bundled in Pango or download the latest version
from the FriBidi project on SourceForge (see Resources). To build GLib, you'll need the libtiff, libgiff, and libjpeg
libraries, the sources of which are also available from the Pango
download page. If you are installing the pre-built GTK+ and GLib RPMs, they are
noted as installing in a different set of directories than the stable
versions, so you can have both installed at once. If you are compiling
the libraries from the source, you can provide a different prefix for
where to install the executables and libraries when you configure the
compilation. The following example shows the sequence for making and
installing Pango, but a similar sequence is used for the other
packages.
Configuring, making, and installing in a non-standard location
cd pango-0.14
./configure --prefix=/usr/local/tkg
make
make install |
Compiling and installing the distribution is straightforward,
particularly on Linux systems that use the RPMSs prepared by Owen
Taylor of Red Hat. Follow the instructions on the Pango download page,
and build and install the packages in this order: GLib-1.3, FriBidi,
Pango, and then GTK+-1.3. If you do install the packages in a non-standard location, make
sure that you add their bin directory to the front of
your path. For example, I put the following in my .bashrc
file:
Add the Pango and GTK+ bin directory to your path
declare -x PATH=/usr/local/tkg/bin:$PATH |
Compiling programs that use Pango
The example programs are written in C, and there is no real
magic in any of the programs. The closest that they come to magic is
when the latter two programs use GTK+ to create a window containing a
label by using just a few lines of code. Even that, however, is
straightforward GTK+ programming that is covered in detail by other
developerWorks articles (see Resources). Possibly the closest that any C program comes to incantations is
in the arguments provided to the compiler and linker so they can find
the right headers and libraries, respectively. Fortunately, GTK+
provides the gtk-config-2.0 program to take the guesswork
out of compiler and linker arguments. (GNOME provides the similar
gnome-config, and Pango has its own
pango-config.) If you're already familiar with using gtk-config with
GTK+, note that gtk-config-2.0 is the version to use with
the development versions of GTK+ 2.0 (and it's the main reason for
putting the extra bin directory on your path).
gtk-config-2.0 --cflags returns arguments for use when
compiling, and gtk-config-2.0 --libs returns arguments
for use when linking. Instead of just running these programs from the
command line -- instructive as that can be -- you can include the
commands in a Makefile within back-ticks (`) so they are
evaluated as part of evaluating a Makefile variable or rule. For
example, I used the following as my Makefile for these programs:
Sample Makefile using gtk-config-2.0
CC=gcc
LDLIBS=`gtk-config-2.0 --libs`
CFLAGS=-Wall -g `gtk-config-2.0 --cflags`
all: attr-table gtk-label pango-label |
Pango attributes in action
In the previous article,
I described Pango's attributes and its rendering pipeline using this example:
<u>car </u><span foreground="blue"><u>is </u>THE CAR</span> in Arabic
|
and showed how Pango turned the marked-up text into a string and a
Pango attribute list. The following program, attr-table.c, shows this in
practice. It declares a string str containing the
marked-up text in the previous example, calls pango_parse_markup() to turn the
marked-up text into a string and an attribute list, and then outputs
the markup for an HTML table that shows the attribute values.
attr-table.c
1 /** attr-table.c **/
2 #include <gtk/gtk.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 int main(int argc, char *argv[])
7 {
8 gchar *str =
9 "<u>car </u><span foreground=\"blue\"><u>is </u>THE CAR</span> in Arabic";
10 gchar *text;
11 PangoAttrList *attrs;
12 PangoAttrIterator *iterator;
13 gint start, end;
14 PangoAttribute *attr;
15
16 pango_parse_markup (str, -1, 0, &attrs, &text, NULL, NULL);
17
18 g_print("<html>\n<head>\n");
19 g_print("<meta http-equiv=\"Content-Type\""
20 "content=\"text/html; charset=\"UTF-8\">\n");
21 g_print("</head>\n<body bgcolor=\"white\">\n");
22 g_print("<table border=\"1\" bgcolor=\"white\">\n");
23
24 iterator = pango_attr_list_get_iterator(attrs);
25
26 g_print("<tr>\n<td>String:</td>");
27 do {
28 pango_attr_iterator_range(iterator, &start, &end);
29
30 g_print("<td>%.*s</td>", MIN((gint)strlen(text), end - start),
31 &text[start]);
32 }
33 while (pango_attr_iterator_next(iterator));
34 g_print("</tr>\n");
35
36 iterator = pango_attr_list_get_iterator(attrs);
37
38 g_print("<tr>\n<td>Foreground:</td>");
39 do {
40 pango_attr_iterator_range(iterator, &start, &end);
41 if ((attr = pango_attr_iterator_get(iterator,
42 PANGO_ATTR_FOREGROUND))) {
43 const PangoAttrColor *color_attr = (const PangoAttrColor *)attr;
44 g_print ("<td>#%04x%04x%04x</td>", color_attr->red,
45 color_attr->green, color_attr->blue);
46 } else {
47 g_print ("<td> </td>");
48 }
49 }
50 while (pango_attr_iterator_next(iterator));
51 g_print("</tr>\n");
52
53 iterator = pango_attr_list_get_iterator(attrs);
54
55 g_print("<tr>\n<td>Underline:</td>");
56 do {
57 pango_attr_iterator_range(iterator, &start, &end);
58
59 attr = pango_attr_iterator_get(iterator, PANGO_ATTR_UNDERLINE);
60
61 if (attr) {
62 g_print ("<td>Yes</td>");
63 } else {
64 g_print ("<td> </td>");
65 }
66 }
67 while (pango_attr_iterator_next(iterator));
68 g_print("</tr>\n");
69
70 g_print("</table>\n</body>\n</html>\n");
71
72 pango_attr_iterator_destroy (iterator);
73
74 exit(0);
75 }
|
attr-table.c in detail
The include statement on line 2 is all that you need to use to
tell the compiler to include the dozens of header files associated
with GTK+ and, since Pango is incorporated into GTK+, it's also all
you need to use Pango-specific functions in your GTK+ program.
str, which contains the marked-up text, is declared in
lines 8 and 9.
16 pango_parse_markup (str, -1, 0, &attrs, &text, NULL, NULL);
|
Line 16 is where the action is. pango_parse_markup()
converts the marked-up text in str into a text string,
which is left in text, and a Pango attribute list, which
is left in attrs. The remaining arguments aren't used in
the example program. In particular, some useful error checking has
been omitted for the sake of brevity. Lines 18 to 22 just print the HTML markup for the start of an HTML
page containing a table.
24 iterator = pango_attr_list_get_iterator(attrs);
25
26 g_print("<tr>\n<td>String:</td>");
27 do {
28 pango_attr_iterator_range(iterator, &start, &end);
29
30 g_print("<td>%.*s</td>", MIN((gint)strlen(text), end - start),
31 &text[start]);
32 }
33 while (pango_attr_iterator_next(iterator));
34 g_print("</tr>\n");
|
A single Pango attribute records the start and end of the span of
text to which it applies, but it is easier to layout text by dealing
with spans of text where each span has the same set of attributes.
pango_attr_list_get_iterator() returns a Pango attribute
iterator that is used to step through the spans of text that have
consistent attributes. This allows the code that is using the iterator
to avoid dealing with overlapping attributes. For example, where two
attributes intersect, the attribute iterator finds three spans: one
with the first attribute, another with both the first and second
attributes, and a third span with just the second attribute. When the attribute iterator is initialized in line 24, it
identifies the first span of text with consistent attributes. The
do-while loop in lines 27 to 33 simply
prints the markup for an HTML table cell containing the portion of
text that is spanned by the current set of Pango
attributes. The pango_attr_iterator_next() in the
while statement advances the iterator to the span of text
with the next style until it returns NULL at the end of
the text. The MIN macro in the g_print statement in
line 30 is necessary because the value of end for the
last span is equal to G_MAXINT, the maximum integer
value, and is not equal to the length of the string. If you're
unfamiliar with g_print(), it's a robust
equivalent of the standard C function printf() and is
provided by GLib, which is used by GTK+. The result of lines 24 to 34 is the HTML markup for a table row
where each cell in the row contains the text spanned by each iteration
of iterator. The attribute iterator is reinitialized in line 36 and is used
in lines 39 to 51 to loop through the text spans again. Once again,
the loop prints the markup for an HTML table cell: If the span has a
foreground color attribute, the cell contains the hexadecimal
representation of the color, and if it doesn't, the cell contains a
no-break space entity. Line 43 casts attr to a Pango color attribute so the
print statement can access the extra parts of a
PangoAttrColor that a common or garden
PangoAttribute structure does not have. There is no harm
in treating the color attribute as an ordinary attribute since
the structures for all Pango attributes begin with the same
members. Lines 53 to 68 are similar to the previous loop, except this time
the code inside the loop tests for the current span of text having an
underline attribute. In real life, a program would be likely to test for all 15 Pango
attribute types, but this program gets away with testing for only two
since the program is tailored for the example. The result is an HTML table showing the spans of text that have
consistent attributes and the attributes for each span:
| String: | car | is | THE CAR | in Arabic | | Foreground: | | #00000000ffff | #00000000ffff | | | Underline: | Yes | Yes | | |
|
A label with Pango
The previous program showed you how
Pango transforms markup into a string and an attribute list,
and how Pango simplifies access to the attribute list. But
laying out text would certainly be a lot of work if you could only
deal with Pango attribute iterators and attribute lists. Fortunately, as part of Pango's integration into GTK+, GTK+ 2.0
understands Pango attribute lists, and you set the attributes of a
label using gtk_label_set_attributes() and a Pango
attribute list.
pango-attr.c, shown below, does just that: It uses
pango_parse_markup() to convert marked-up text to a
string and an attribute list, then it creates a label showing the text
and sets the label's attributes with the attribute list. The program
also creates a window to hold the label and connects two callback
functions to the window so the program exits cleanly when you close
the window. The previous example used uppercase text to represent right-to-left
text such as Arabic or Hebrew. This convention is used by the Unicode
Standard and UAX #9 when giving examples of bidirectional ordering,
and the FriBidi test program even has an option for treating uppercase
text this way so the program can be tested against the examples in the
Unicode Standard. GTK+, however, doesn't provide the option (nor
should it), so pango-attr.c just uses the Latin letters
"a" to "m" in place of the lowercase text in the previous example, and
uses the first five letters from the Hebrew block in place of the
uppercase text. The Hebrew characters are represented in the program
by XML-style hexadecimal numeric character references, which
pango_parse_markup() understands. Note that the right-to-left text is a two-letter word followed by
a three-letter word instead of the two three-letter words in the
previous example. Even if you (like me) don't read Hebrew and don't
have the Unicode Standard handy to check which character is which,
when you look at the window produced by the program, you'll still be
able to see that the two-letter word is to the right of the
three-letter word when the text is displayed in a window.
pango-label.c
1 /** pango-label.c **/
2 #include <gtk/gtk.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 gint eventDelete(GtkWidget *widget, GdkEvent *event,gpointer data)
7 {
8 return(FALSE);
9 }
10
11 gint eventDestroy(GtkWidget *widget, GdkEvent *event,gpointer data)
12 {
13 gtk_main_quit();
14 return(0);
15 }
16
17
18 int main(int argc, char *argv[])
19 {
20 GtkWidget *window;
21 GtkWidget *label;
22 gchar *str = "<u>abc </u><span foreground=\"blue\"><u>de </u>"
23 "אב גדה</span> fg hijklm";
24 gchar *text;
25 PangoAttrList *attrs;
26
27 gtk_init(&argc,&argv);
28
29 pango_parse_markup (str, -1,0, &attrs, &text, NULL, NULL);
30
31 label = gtk_label_new(text);
32 gtk_label_set_attributes(GTK_LABEL(label), attrs);
33
34 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
35 gtk_container_add(GTK_CONTAINER(window), label);
36 gtk_window_set_title (GTK_WINDOW (window), "Pango Label");
37 gtk_container_set_border_width(GTK_CONTAINER(window), 30);
38 gtk_widget_show_all(window);
39
40 gtk_signal_connect(GTK_OBJECT(window), "delete_event",
41 GTK_SIGNAL_FUNC(eventDelete), NULL);
42 gtk_signal_connect(GTK_OBJECT(window), "destroy",
43 GTK_SIGNAL_FUNC(eventDestroy), NULL);
44
45 gtk_main();
46 exit(0);
47 }
|
When you compile this program using the development version of GTK+
and then run it, you will see the window on the screen, but you will
also get a warning message on your console or terminal warning you
against using the development version of GTK+. If you've gotten
this far, you know that you are using the development version, so you can
safely ignore the message. The output of the program is a window containing the styled text: Label styled by Pango

GTK+ only
There is an even easier way. Since this article series is about
Pango, I've concentrated on how Pango works, but Pango is so
thoroughly integrated into GTK+ that you can create formatted text in
a label directly from the same marked-up text that you've seen used
with pango_parse_markup().
gtk-label.c, which isn't being shown in its entirety,
is almost identical to pango-label.c, except that this
program uses gtk_label_set_markup() instead of the
combination of pango_parse_markup() and
gtk_label_set_attributes(). The changed lines are shown
below:
Portion of gtk-label.c
27 label = gtk_label_new(NULL);
28 gtk_label_set_markup(GTK_LABEL(label), str);
|
The only other difference between the programs is a changed window
title. The output of the program is a window containing the same styled
text: Label styled by GTK+

Summary
This article and the previous article have shown you how to use Pango. In
particular, they've shown you the 15 types of text attributes that
Pango supports and how to specify the text attributes using a simple
markup scheme. The first article describes and this second article
demonstrates how you can use the pango_parse_markup()
function to convert the markup into Pango text attributes. This second
article also shows how you can take advantage of Pango's integration
into GTK+ and use the markup with gtk_label_set_markup()
to directly specify a GTK+ label widget's appearance. The example in this article shows Pango successfully laying out a
mixture of right-to-left and left-to-right text, and the screenshots
in the first article show text in multiple languages -- including
Arabic, Greek, Hebrew, Japanese, and Russian -- being laid out by
Pango. Based on your knowledge of Pango attributes and how to use the
markup scheme, you will now be able to do the same with the text in your
GTK+ and GNOME programs.
Resources
About the author  | |  | Tony Graham is the author of
Unicode: A Primer
, the first and currently only book about the Unicode Standard, Version 3.0, and its uses. An Australian, Tony is a Specialist member of the Unicode Consortium. He can be reached at tkg@menteith.com.
|
Rate this page
|  |