1 /* Rotated Text
2 *
3 * This demo shows how to use PangoCairo to draw rotated and transformed
4 * text. The right pane shows a rotated GtkLabel widget.
5 *
6 * In both cases, a custom PangoCairo shape renderer is installed to draw
7 * a red heard using cairo drawing operations instead of the Unicode heart
8 * character.
9 */
10
11 #include <gtk/gtk.h>
12 #include <string.h>
13
14 static GtkWidget *window = NULL;
15
16 #define HEART "♥"
17 const char text[] = "I ♥ GTK+";
18
19 static void
fancy_shape_renderer(cairo_t * cr,PangoAttrShape * attr,gboolean do_path,gpointer data)20 fancy_shape_renderer (cairo_t *cr,
21 PangoAttrShape *attr,
22 gboolean do_path,
23 gpointer data)
24 {
25 double x, y;
26 cairo_get_current_point (cr, &x, &y);
27 cairo_translate (cr, x, y);
28
29 cairo_scale (cr,
30 (double) attr->ink_rect.width / PANGO_SCALE,
31 (double) attr->ink_rect.height / PANGO_SCALE);
32
33 switch (GPOINTER_TO_UINT (attr->data))
34 {
35 case 0x2665: /* U+2665 BLACK HEART SUIT */
36 {
37 cairo_move_to (cr, .5, .0);
38 cairo_line_to (cr, .9, -.4);
39 cairo_curve_to (cr, 1.1, -.8, .5, -.9, .5, -.5);
40 cairo_curve_to (cr, .5, -.9, -.1, -.8, .1, -.4);
41 cairo_close_path (cr);
42 }
43 break;
44 }
45
46 if (!do_path) {
47 cairo_set_source_rgb (cr, 1., 0., 0.);
48 cairo_fill (cr);
49 }
50 }
51
52 PangoAttrList *
create_fancy_attr_list_for_layout(PangoLayout * layout)53 create_fancy_attr_list_for_layout (PangoLayout *layout)
54 {
55 PangoAttrList *attrs;
56 PangoFontMetrics *metrics;
57 int ascent;
58 PangoRectangle ink_rect, logical_rect;
59 const char *p;
60
61 /* Get font metrics and prepare fancy shape size */
62 metrics = pango_context_get_metrics (pango_layout_get_context (layout),
63 pango_layout_get_font_description (layout),
64 NULL);
65 ascent = pango_font_metrics_get_ascent (metrics);
66 logical_rect.x = 0;
67 logical_rect.width = ascent;
68 logical_rect.y = -ascent;
69 logical_rect.height = ascent;
70 ink_rect = logical_rect;
71 pango_font_metrics_unref (metrics);
72
73 /* Set fancy shape attributes for all hearts */
74 attrs = pango_attr_list_new ();
75 for (p = text; (p = strstr (p, HEART)); p += strlen (HEART))
76 {
77 PangoAttribute *attr;
78
79 attr = pango_attr_shape_new_with_data (&ink_rect,
80 &logical_rect,
81 GUINT_TO_POINTER (g_utf8_get_char (p)),
82 NULL, NULL);
83
84 attr->start_index = p - text;
85 attr->end_index = attr->start_index + strlen (HEART);
86
87 pango_attr_list_insert (attrs, attr);
88 }
89
90 return attrs;
91 }
92
93 static gboolean
rotated_text_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer data)94 rotated_text_expose_event (GtkWidget *widget,
95 GdkEventExpose *event,
96 gpointer data)
97 {
98 #define RADIUS 150
99 #define N_WORDS 5
100 #define FONT "Serif 18"
101
102 PangoContext *context;
103 PangoLayout *layout;
104 PangoFontDescription *desc;
105
106 cairo_t *cr;
107 cairo_pattern_t *pattern;
108
109 PangoAttrList *attrs;
110 GtkAllocation allocation;
111 int width, height;
112 double device_radius;
113 int i;
114
115 gtk_widget_get_allocation (widget, &allocation);
116
117 width = allocation.width;
118 height = allocation.height;
119
120 /* Create a cairo context and set up a transformation matrix so that the user
121 * space coordinates for the centered square where we draw are [-RADIUS, RADIUS],
122 * [-RADIUS, RADIUS].
123 * We first center, then change the scale. */
124 cr = gdk_cairo_create (gtk_widget_get_window (widget));
125 device_radius = MIN (width, height) / 2.;
126 cairo_translate (cr,
127 device_radius + (width - 2 * device_radius) / 2,
128 device_radius + (height - 2 * device_radius) / 2);
129 cairo_scale (cr, device_radius / RADIUS, device_radius / RADIUS);
130
131 /* Create and a subtle gradient source and use it. */
132 pattern = cairo_pattern_create_linear (-RADIUS, -RADIUS, RADIUS, RADIUS);
133 cairo_pattern_add_color_stop_rgb (pattern, 0., .5, .0, .0);
134 cairo_pattern_add_color_stop_rgb (pattern, 1., .0, .0, .5);
135 cairo_set_source (cr, pattern);
136
137 /* Create a PangoContext and set up our shape renderer */
138 context = gtk_widget_create_pango_context (widget);
139 pango_cairo_context_set_shape_renderer (context,
140 fancy_shape_renderer,
141 NULL, NULL);
142
143 /* Create a PangoLayout, set the text, font, and attributes */
144 layout = pango_layout_new (context);
145 pango_layout_set_text (layout, text, -1);
146 desc = pango_font_description_from_string (FONT);
147 pango_layout_set_font_description (layout, desc);
148
149 attrs = create_fancy_attr_list_for_layout (layout);
150 pango_layout_set_attributes (layout, attrs);
151 pango_attr_list_unref (attrs);
152
153 /* Draw the layout N_WORDS times in a circle */
154 for (i = 0; i < N_WORDS; i++)
155 {
156 int width, height;
157
158 /* Inform Pango to re-layout the text with the new transformation matrix */
159 pango_cairo_update_layout (cr, layout);
160
161 pango_layout_get_pixel_size (layout, &width, &height);
162 cairo_move_to (cr, - width / 2, - RADIUS * .9);
163 pango_cairo_show_layout (cr, layout);
164
165 /* Rotate for the next turn */
166 cairo_rotate (cr, G_PI*2 / N_WORDS);
167 }
168
169 /* free the objects we created */
170 pango_font_description_free (desc);
171 g_object_unref (layout);
172 g_object_unref (context);
173 cairo_pattern_destroy (pattern);
174 cairo_destroy (cr);
175
176 return FALSE;
177 }
178
179 GtkWidget *
do_rotated_text(GtkWidget * do_widget)180 do_rotated_text (GtkWidget *do_widget)
181 {
182 if (!window)
183 {
184 GtkWidget *box;
185 GtkWidget *drawing_area;
186 GtkWidget *label;
187 PangoLayout *layout;
188 PangoAttrList *attrs;
189
190 const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
191
192 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
193 gtk_window_set_screen (GTK_WINDOW (window),
194 gtk_widget_get_screen (do_widget));
195 gtk_window_set_title (GTK_WINDOW (window), "Rotated Text");
196 gtk_window_set_default_size (GTK_WINDOW (window), 4 * RADIUS, 2 * RADIUS);
197 g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window);
198
199 box = gtk_hbox_new (TRUE, 0);
200 gtk_container_add (GTK_CONTAINER (window), box);
201
202 /* Add a drawing area */
203
204 drawing_area = gtk_drawing_area_new ();
205 gtk_container_add (GTK_CONTAINER (box), drawing_area);
206
207 /* This overrides the background color from the theme */
208 gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white);
209
210 g_signal_connect (drawing_area, "expose-event",
211 G_CALLBACK (rotated_text_expose_event), NULL);
212
213 /* And a label */
214
215 label = gtk_label_new (text);
216 gtk_container_add (GTK_CONTAINER (box), label);
217
218 gtk_label_set_angle (GTK_LABEL (label), 45);
219
220 /* Set up fancy stuff on the label */
221 layout = gtk_label_get_layout (GTK_LABEL (label));
222 pango_cairo_context_set_shape_renderer (pango_layout_get_context (layout),
223 fancy_shape_renderer,
224 NULL, NULL);
225 attrs = create_fancy_attr_list_for_layout (layout);
226 gtk_label_set_attributes (GTK_LABEL (label), attrs);
227 pango_attr_list_unref (attrs);
228 }
229
230 if (!gtk_widget_get_visible (window))
231 {
232 gtk_widget_show_all (window);
233 }
234 else
235 {
236 gtk_widget_destroy (window);
237 window = NULL;
238 }
239
240 return window;
241 }
242