1/* GDK - The GIMP Drawing Kit
2 * Copyright (C) 2000 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19
20#include "gdkpango.h"
21
22#include "gdkscreen.h"
23#include "gdkintl.h"
24
25#include <math.h>
26#include <pango/pangocairo.h>
27
28
29/**
30 * SECTION:pango_interaction
31 * @Short_description: Using Pango in GDK
32 * @Title: Pango Interaction
33 *
34 * Pango is the text layout system used by GDK and GTK+. The functions
35 * and types in this section are used to obtain clip regions for
36 * #PangoLayouts, and to get #PangoContexts that can be used with
37 * GDK.
38 *
39 * Creating a #PangoLayout object is the first step in rendering text,
40 * and requires getting a handle to a #PangoContext. For GTK+ programs,
41 * you’ll usually want to use gtk_widget_get_pango_context(), or
42 * gtk_widget_create_pango_layout(), rather than using the lowlevel
43 * gdk_pango_context_get_for_screen(). Once you have a #PangoLayout, you
44 * can set the text and attributes of it with Pango functions like
45 * pango_layout_set_text() and get its size with pango_layout_get_size().
46 * (Note that Pango uses a fixed point system internally, so converting
47 * between Pango units and pixels using [PANGO_SCALE][PANGO-SCALE-CAPS]
48 * or the PANGO_PIXELS() macro.)
49 *
50 * Rendering a Pango layout is done most simply with pango_cairo_show_layout();
51 * you can also draw pieces of the layout with pango_cairo_show_layout_line().
52 *
53 * ## Draw transformed text with Pango and cairo ## {#rotated-example}
54 *
55 * |[<!-- language="C" -->
56 * #define RADIUS 100
57 * #define N_WORDS 10
58 * #define FONT "Sans Bold 18"
59 *
60 * PangoContext *context;
61 * PangoLayout *layout;
62 * PangoFontDescription *desc;
63 *
64 * double radius;
65 * int width, height;
66 * int i;
67 *
68 * // Set up a transformation matrix so that the user space coordinates for
69 * // where we are drawing are [-RADIUS, RADIUS], [-RADIUS, RADIUS]
70 * // We first center, then change the scale
71 *
72 * width = gdk_window_get_width (window);
73 * height = gdk_window_get_height (window);
74 * radius = MIN (width, height) / 2.;
75 *
76 * cairo_translate (cr,
77 * radius + (width - 2 * radius) / 2,
78 * radius + (height - 2 * radius) / 2);
79 * cairo_scale (cr, radius / RADIUS, radius / RADIUS);
80 *
81 * // Create a PangoLayout, set the font and text
82 * context = gdk_pango_context_get_for_screen (screen);
83 * layout = pango_layout_new (context);
84 * pango_layout_set_text (layout, "Text", -1);
85 * desc = pango_font_description_from_string (FONT);
86 * pango_layout_set_font_description (layout, desc);
87 * pango_font_description_free (desc);
88 *
89 * // Draw the layout N_WORDS times in a circle
90 * for (i = 0; i < N_WORDS; i++)
91 * {
92 * double red, green, blue;
93 * double angle = 2 * G_PI * i / n_words;
94 *
95 * cairo_save (cr);
96 *
97 * // Gradient from red at angle == 60 to blue at angle == 300
98 * red = (1 + cos (angle - 60)) / 2;
99 * green = 0;
100 * blue = 1 - red;
101 *
102 * cairo_set_source_rgb (cr, red, green, blue);
103 * cairo_rotate (cr, angle);
104 *
105 * // Inform Pango to re-layout the text with the new transformation matrix
106 * pango_cairo_update_layout (cr, layout);
107 *
108 * pango_layout_get_size (layout, &width, &height);
109 *
110 * cairo_move_to (cr, - width / 2 / PANGO_SCALE, - DEFAULT_TEXT_RADIUS);
111 * pango_cairo_show_layout (cr, layout);
112 *
113 * cairo_restore (cr);
114 * }
115 *
116 * g_object_unref (layout);
117 * g_object_unref (context);
118 * ]|
119 *
120 * ## Output of the [example][rotated-example] above.
121 *
122 * ![](rotated-text.png)
123 */
124
125/* Get a clip region to draw only part of a layout. index_ranges
126 * contains alternating range starts/stops. The region is the
127 * region which contains the given ranges, i.e. if you draw with the
128 * region as clip, only the given ranges are drawn.
129 */
130static cairo_region_t*
131layout_iter_get_line_clip_region (PangoLayoutIter *iter,
132 gint x_origin,
133 gint y_origin,
134 const gint *index_ranges,
135 gint n_ranges)
136{
137 PangoLayoutLine *line;
138 cairo_region_t *clip_region;
139 PangoRectangle logical_rect;
140 gint baseline;
141 gint i;
142
143 line = pango_layout_iter_get_line_readonly (iter);
144
145 clip_region = cairo_region_create ();
146
147 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
148 baseline = pango_layout_iter_get_baseline (iter);
149
150 i = 0;
151 while (i < n_ranges)
152 {
153 gint *pixel_ranges = NULL;
154 gint n_pixel_ranges = 0;
155 gint j;
156
157 /* Note that get_x_ranges returns layout coordinates
158 */
159 if (index_ranges[i*2+1] >= line->start_index &&
160 index_ranges[i*2] < line->start_index + line->length)
161 pango_layout_line_get_x_ranges (line,
162 index_ranges[i*2],
163 index_ranges[i*2+1],
164 &pixel_ranges, &n_pixel_ranges);
165
166 for (j = 0; j < n_pixel_ranges; j++)
167 {
168 GdkRectangle rect;
169 int x_off, y_off;
170
171 x_off = PANGO_PIXELS (pixel_ranges[2*j] - logical_rect.x);
172 y_off = PANGO_PIXELS (baseline - logical_rect.y);
173
174 rect.x = x_origin + x_off;
175 rect.y = y_origin - y_off;
176 rect.width = PANGO_PIXELS (pixel_ranges[2*j + 1] - logical_rect.x) - x_off;
177 rect.height = PANGO_PIXELS (baseline - logical_rect.y + logical_rect.height) - y_off;
178
179 cairo_region_union_rectangle (clip_region, &rect);
180 }
181
182 g_free (pixel_ranges);
183 ++i;
184 }
185 return clip_region;
186}
187
188/**
189 * gdk_pango_layout_line_get_clip_region: (skip)
190 * @line: a #PangoLayoutLine
191 * @x_origin: X pixel where you intend to draw the layout line with this clip
192 * @y_origin: baseline pixel where you intend to draw the layout line with this clip
193 * @index_ranges: (array): array of byte indexes into the layout,
194 * where even members of array are start indexes and odd elements
195 * are end indexes
196 * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
197 *
198 * Obtains a clip region which contains the areas where the given
199 * ranges of text would be drawn. @x_origin and @y_origin are the top left
200 * position of the layout. @index_ranges
201 * should contain ranges of bytes in the layout’s text. The clip
202 * region will include space to the left or right of the line (to the
203 * layout bounding box) if you have indexes above or below the indexes
204 * contained inside the line. This is to draw the selection all the way
205 * to the side of the layout. However, the clip region is in line coordinates,
206 * not layout coordinates.
207 *
208 * Note that the regions returned correspond to logical extents of the text
209 * ranges, not ink extents. So the drawn line may in fact touch areas out of
210 * the clip region. The clip region is mainly useful for highlightling parts
211 * of text, such as when text is selected.
212 *
213 * Returns: a clip region containing the given ranges
214 **/
215cairo_region_t*
216gdk_pango_layout_line_get_clip_region (PangoLayoutLine *line,
217 gint x_origin,
218 gint y_origin,
219 const gint *index_ranges,
220 gint n_ranges)
221{
222 cairo_region_t *clip_region;
223 PangoLayoutIter *iter;
224
225 g_return_val_if_fail (line != NULL, NULL);
226 g_return_val_if_fail (index_ranges != NULL, NULL);
227
228 iter = pango_layout_get_iter (line->layout);
229 while (pango_layout_iter_get_line_readonly (iter) != line)
230 pango_layout_iter_next_line (iter);
231
232 clip_region = layout_iter_get_line_clip_region(iter, x_origin, y_origin, index_ranges, n_ranges);
233
234 pango_layout_iter_free (iter);
235
236 return clip_region;
237}
238
239/**
240 * gdk_pango_layout_get_clip_region: (skip)
241 * @layout: a #PangoLayout
242 * @x_origin: X pixel where you intend to draw the layout with this clip
243 * @y_origin: Y pixel where you intend to draw the layout with this clip
244 * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
245 * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
246 *
247 * Obtains a clip region which contains the areas where the given ranges
248 * of text would be drawn. @x_origin and @y_origin are the top left point
249 * to center the layout. @index_ranges should contain
250 * ranges of bytes in the layout’s text.
251 *
252 * Note that the regions returned correspond to logical extents of the text
253 * ranges, not ink extents. So the drawn layout may in fact touch areas out of
254 * the clip region. The clip region is mainly useful for highlightling parts
255 * of text, such as when text is selected.
256 *
257 * Returns: a clip region containing the given ranges
258 **/
259cairo_region_t*
260gdk_pango_layout_get_clip_region (PangoLayout *layout,
261 gint x_origin,
262 gint y_origin,
263 const gint *index_ranges,
264 gint n_ranges)
265{
266 PangoLayoutIter *iter;
267 cairo_region_t *clip_region;
268
269 g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
270 g_return_val_if_fail (index_ranges != NULL, NULL);
271
272 clip_region = cairo_region_create ();
273
274 iter = pango_layout_get_iter (layout);
275
276 do
277 {
278 PangoRectangle logical_rect;
279 cairo_region_t *line_region;
280 gint baseline;
281
282 pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
283 baseline = pango_layout_iter_get_baseline (iter);
284
285 line_region = layout_iter_get_line_clip_region(iter,
286 x_origin + PANGO_PIXELS (logical_rect.x),
287 y_origin + PANGO_PIXELS (baseline),
288 index_ranges,
289 n_ranges);
290
291 cairo_region_union (clip_region, line_region);
292 cairo_region_destroy (line_region);
293 }
294 while (pango_layout_iter_next_line (iter));
295
296 pango_layout_iter_free (iter);
297
298 return clip_region;
299}
300
301/**
302 * gdk_pango_context_get:
303 *
304 * Creates a #PangoContext for the default GDK screen.
305 *
306 * The context must be freed when you’re finished with it.
307 *
308 * When using GTK+, normally you should use gtk_widget_get_pango_context()
309 * instead of this function, to get the appropriate context for
310 * the widget you intend to render text onto.
311 *
312 * The newly created context will have the default font options (see
313 * #cairo_font_options_t) for the default screen; if these options
314 * change it will not be updated. Using gtk_widget_get_pango_context()
315 * is more convenient if you want to keep a context around and track
316 * changes to the screen’s font rendering settings.
317 *
318 * Returns: (transfer full): a new #PangoContext for the default display
319 **/
320PangoContext *
321gdk_pango_context_get (void)
322{
323 return gdk_pango_context_get_for_display (gdk_display_get_default ());
324}
325
326/**
327 * gdk_pango_context_get_for_screen:
328 * @screen: the #GdkScreen for which the context is to be created.
329 *
330 * Creates a #PangoContext for @screen.
331 *
332 * The context must be freed when you’re finished with it.
333 *
334 * When using GTK+, normally you should use gtk_widget_get_pango_context()
335 * instead of this function, to get the appropriate context for
336 * the widget you intend to render text onto.
337 *
338 * The newly created context will have the default font options
339 * (see #cairo_font_options_t) for the screen; if these options
340 * change it will not be updated. Using gtk_widget_get_pango_context()
341 * is more convenient if you want to keep a context around and track
342 * changes to the screen’s font rendering settings.
343 *
344 * Returns: (transfer full): a new #PangoContext for @screen
345 *
346 * Since: 2.2
347 **/
348PangoContext *
349gdk_pango_context_get_for_screen (GdkScreen *screen)
350{
351 PangoFontMap *fontmap;
352 PangoContext *context;
353 const cairo_font_options_t *options;
354 double dpi;
355
356 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
357
358 fontmap = pango_cairo_font_map_get_default ();
359 context = pango_font_map_create_context (fontmap);
360
361 options = gdk_screen_get_font_options (screen);
362 pango_cairo_context_set_font_options (context, options);
363
364 dpi = gdk_screen_get_resolution (screen);
365 pango_cairo_context_set_resolution (context, dpi);
366
367 return context;
368}
369
370/**
371 * gdk_pango_context_get_for_display:
372 * @display: the #GdkDisplay for which the context is to be created
373 *
374 * Creates a #PangoContext for @display.
375 *
376 * The context must be freed when you’re finished with it.
377 *
378 * When using GTK+, normally you should use gtk_widget_get_pango_context()
379 * instead of this function, to get the appropriate context for
380 * the widget you intend to render text onto.
381 *
382 * The newly created context will have the default font options
383 * (see #cairo_font_options_t) for the display; if these options
384 * change it will not be updated. Using gtk_widget_get_pango_context()
385 * is more convenient if you want to keep a context around and track
386 * changes to the font rendering settings.
387 *
388 * Returns: (transfer full): a new #PangoContext for @display
389 *
390 * Since: 3.22
391 */
392PangoContext *
393gdk_pango_context_get_for_display (GdkDisplay *display)
394{
395 g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
396
397 return gdk_pango_context_get_for_screen (gdk_display_get_default_screen (display));
398}
399