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