Skip to main content

skip to main content

developerWorks  >  Java technology | Linux | Open source  >

The Pango connection: Part 2

Pango in practice

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


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



Back to top


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



Back to top


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

  2 #include <gtk/gtk.h>

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:YesYes  


Back to top


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
Styled text label in a GTK+ window


Back to top


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+
Styled text label in a GTK+ window


Back to top


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


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top