1 | /* Pango/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 heart using cairo drawing operations instead of the Unicode heart |
8 | * character. |
9 | */ |
10 | |
11 | #include <gtk/gtk.h> |
12 | #include <string.h> |
13 | |
14 | #define HEART "♥" |
15 | const char text[] = "I ♥ GTK" ; |
16 | |
17 | static void |
18 | fancy_shape_renderer (cairo_t *cr, |
19 | PangoAttrShape *attr, |
20 | gboolean do_path, |
21 | gpointer data) |
22 | { |
23 | double x, y; |
24 | cairo_get_current_point (cr, x: &x, y: &y); |
25 | cairo_translate (cr, tx: x, ty: y); |
26 | |
27 | cairo_scale (cr, |
28 | sx: (double) attr->ink_rect.width / PANGO_SCALE, |
29 | sy: (double) attr->ink_rect.height / PANGO_SCALE); |
30 | |
31 | if (GPOINTER_TO_UINT (attr->data) == 0x2665) /* U+2665 BLACK HEART SUIT */ |
32 | { |
33 | cairo_move_to (cr, x: .5, y: .0); |
34 | cairo_line_to (cr, x: .9, y: -.4); |
35 | cairo_curve_to (cr, x1: 1.1, y1: -.8, x2: .5, y2: -.9, x3: .5, y3: -.5); |
36 | cairo_curve_to (cr, x1: .5, y1: -.9, x2: -.1, y2: -.8, x3: .1, y3: -.4); |
37 | cairo_close_path (cr); |
38 | } |
39 | |
40 | if (!do_path) |
41 | { |
42 | cairo_set_source_rgb (cr, red: 1., green: 0., blue: 0.); |
43 | cairo_fill (cr); |
44 | } |
45 | } |
46 | |
47 | static PangoAttrList * |
48 | create_fancy_attr_list_for_layout (PangoLayout *layout) |
49 | { |
50 | PangoAttrList *attrs; |
51 | PangoFontMetrics *metrics; |
52 | int ascent; |
53 | PangoRectangle ink_rect, logical_rect; |
54 | const char *p; |
55 | |
56 | /* Get font metrics and prepare fancy shape size */ |
57 | metrics = pango_context_get_metrics (context: pango_layout_get_context (layout), |
58 | desc: pango_layout_get_font_description (layout), |
59 | NULL); |
60 | ascent = pango_font_metrics_get_ascent (metrics); |
61 | logical_rect.x = 0; |
62 | logical_rect.width = ascent; |
63 | logical_rect.y = -ascent; |
64 | logical_rect.height = ascent; |
65 | ink_rect = logical_rect; |
66 | pango_font_metrics_unref (metrics); |
67 | |
68 | /* Set fancy shape attributes for all hearts */ |
69 | attrs = pango_attr_list_new (); |
70 | for (p = text; (p = strstr (haystack: p, HEART)); p += strlen (HEART)) |
71 | { |
72 | PangoAttribute *attr; |
73 | |
74 | attr = pango_attr_shape_new_with_data (ink_rect: &ink_rect, |
75 | logical_rect: &logical_rect, |
76 | GUINT_TO_POINTER (g_utf8_get_char (p)), |
77 | NULL, NULL); |
78 | |
79 | attr->start_index = p - text; |
80 | attr->end_index = attr->start_index + strlen (HEART); |
81 | |
82 | pango_attr_list_insert (list: attrs, attr); |
83 | } |
84 | |
85 | return attrs; |
86 | } |
87 | |
88 | static void |
89 | rotated_text_draw (GtkDrawingArea *da, |
90 | cairo_t *cr, |
91 | int width, |
92 | int height, |
93 | gpointer data) |
94 | { |
95 | #define RADIUS 150 |
96 | #define N_WORDS 5 |
97 | #define FONT "Serif 18" |
98 | |
99 | PangoContext *context; |
100 | PangoLayout *layout; |
101 | PangoFontDescription *desc; |
102 | |
103 | cairo_pattern_t *pattern; |
104 | |
105 | PangoAttrList *attrs; |
106 | |
107 | double device_radius; |
108 | int i; |
109 | |
110 | /* Create a cairo context and set up a transformation matrix so that the user |
111 | * space coordinates for the centered square where we draw are [-RADIUS, RADIUS], |
112 | * [-RADIUS, RADIUS]. |
113 | * We first center, then change the scale. */ |
114 | device_radius = MIN (width, height) / 2.; |
115 | cairo_translate (cr, |
116 | tx: device_radius + (width - 2 * device_radius) / 2, |
117 | ty: device_radius + (height - 2 * device_radius) / 2); |
118 | cairo_scale (cr, sx: device_radius / RADIUS, sy: device_radius / RADIUS); |
119 | |
120 | /* Create and a subtle gradient source and use it. */ |
121 | pattern = cairo_pattern_create_linear (x0: -RADIUS, y0: -RADIUS, RADIUS, RADIUS); |
122 | cairo_pattern_add_color_stop_rgb (pattern, offset: 0., red: .5, green: .0, blue: .0); |
123 | cairo_pattern_add_color_stop_rgb (pattern, offset: 1., red: .0, green: .0, blue: .5); |
124 | cairo_set_source (cr, source: pattern); |
125 | |
126 | /* Create a PangoContext and set up our shape renderer */ |
127 | context = gtk_widget_create_pango_context (GTK_WIDGET (da)); |
128 | pango_cairo_context_set_shape_renderer (context, |
129 | func: fancy_shape_renderer, |
130 | NULL, NULL); |
131 | |
132 | /* Create a PangoLayout, set the text, font, and attributes */ |
133 | layout = pango_layout_new (context); |
134 | pango_layout_set_text (layout, text, length: -1); |
135 | desc = pango_font_description_from_string (FONT); |
136 | pango_layout_set_font_description (layout, desc); |
137 | |
138 | attrs = create_fancy_attr_list_for_layout (layout); |
139 | pango_layout_set_attributes (layout, attrs); |
140 | pango_attr_list_unref (list: attrs); |
141 | |
142 | /* Draw the layout N_WORDS times in a circle */ |
143 | for (i = 0; i < N_WORDS; i++) |
144 | { |
145 | int layout_width, layout_height; |
146 | |
147 | /* Inform Pango to re-layout the text with the new transformation matrix */ |
148 | pango_cairo_update_layout (cr, layout); |
149 | |
150 | pango_layout_get_pixel_size (layout, width: &layout_width, height: &layout_height); |
151 | cairo_move_to (cr, x: - layout_width / 2, y: - RADIUS * .9); |
152 | pango_cairo_show_layout (cr, layout); |
153 | |
154 | /* Rotate for the next turn */ |
155 | cairo_rotate (cr, G_PI*2 / N_WORDS); |
156 | } |
157 | |
158 | /* free the objects we created */ |
159 | pango_font_description_free (desc); |
160 | g_object_unref (object: layout); |
161 | g_object_unref (object: context); |
162 | cairo_pattern_destroy (pattern); |
163 | } |
164 | |
165 | GtkWidget * |
166 | do_rotated_text (GtkWidget *do_widget) |
167 | { |
168 | static GtkWidget *window = NULL; |
169 | |
170 | if (!window) |
171 | { |
172 | GtkWidget *box; |
173 | GtkWidget *drawing_area; |
174 | GtkWidget *label; |
175 | PangoLayout *layout; |
176 | PangoAttrList *attrs; |
177 | |
178 | window = gtk_window_new (); |
179 | gtk_window_set_display (GTK_WINDOW (window), |
180 | display: gtk_widget_get_display (widget: do_widget)); |
181 | gtk_window_set_title (GTK_WINDOW (window), title: "Rotated Text" ); |
182 | gtk_window_set_default_size (GTK_WINDOW (window), width: 4 * RADIUS, height: 2 * RADIUS); |
183 | g_object_add_weak_pointer (G_OBJECT (window), weak_pointer_location: (gpointer *)&window); |
184 | |
185 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
186 | gtk_box_set_homogeneous (GTK_BOX (box), TRUE); |
187 | gtk_window_set_child (GTK_WINDOW (window), child: box); |
188 | |
189 | /* Add a drawing area */ |
190 | drawing_area = gtk_drawing_area_new (); |
191 | gtk_box_append (GTK_BOX (box), child: drawing_area); |
192 | gtk_widget_add_css_class (widget: drawing_area, css_class: "view" ); |
193 | |
194 | gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), |
195 | draw_func: rotated_text_draw, |
196 | NULL, NULL); |
197 | |
198 | /* And a label */ |
199 | label = gtk_label_new (str: text); |
200 | gtk_box_append (GTK_BOX (box), child: label); |
201 | |
202 | /* Set up fancy stuff on the label */ |
203 | layout = gtk_label_get_layout (GTK_LABEL (label)); |
204 | pango_cairo_context_set_shape_renderer (context: pango_layout_get_context (layout), |
205 | func: fancy_shape_renderer, |
206 | NULL, NULL); |
207 | attrs = create_fancy_attr_list_for_layout (layout); |
208 | gtk_label_set_attributes (GTK_LABEL (label), attrs); |
209 | pango_attr_list_unref (list: attrs); |
210 | } |
211 | |
212 | if (!gtk_widget_get_visible (widget: window)) |
213 | gtk_widget_show (widget: window); |
214 | else |
215 | gtk_window_destroy (GTK_WINDOW (window)); |
216 | |
217 | return window; |
218 | } |
219 | |