gdkpango.c 12.8 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GDK - The GIMP Drawing Kit
2 3 4
 * Copyright (C) 2000 Red Hat, Inc. 
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 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
Javier Jardón's avatar
Javier Jardón committed
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 26 27
#include <math.h>
#include <pango/pangocairo.h>

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

/**
 * SECTION:pango_interaction
 * @Short_description: Using Pango in GDK
 * @Title: Pango Interaction
 *
 * Pango is the text layout system used by GDK and GTK+. The functions
 * and types in this section are used to obtain clip regions for
 * #PangoLayouts, and to get #PangoContexts that can be used with
 * GDK.
 *
 * Creating a #PangoLayout object is the first step in rendering text,
 * and requires getting a handle to a #PangoContext. For GTK+ programs,
 * you'll usually want to use gtk_widget_get_pango_context(), or
 * gtk_widget_create_pango_layout(), rather than using the lowlevel
 * gdk_pango_context_get_for_screen(). Once you have a #PangoLayout, you
 * can set the text and attributes of it with Pango functions like
 * pango_layout_set_text() and get its size with pango_layout_get_size().
 * (Note that Pango uses a fixed point system internally, so converting
 * between Pango units and pixels using <link
 * linkend="PANGO-SCALE-CAPS">PANGO_SCALE</link> or the PANGO_PIXELS() macro.)
 *
 * Rendering a Pango layout is done most simply with pango_cairo_show_layout();
 * you can also draw pieces of the layout with pango_cairo_show_layout_line().
 * <example id="rotated-example">
 * <title>Draw transformed text with Pango and cairo</title>
 * <!-- Note that this example is basically the same as
 *      demos/gtk-demo/rotated_text.c -->
 * <programlisting>
 * #define RADIUS 100
 * #define N_WORDS 10
 * #define FONT "Sans Bold 18"
 *
 * PangoContext *context;
 * PangoLayout *layout;
 * PangoFontDescription *desc;
 *
 * double radius;
 * int width, height;
 * int i;
 *
 * /<!---->* Set up a transformation matrix so that the user space coordinates for
 *  * where we are drawing are [-RADIUS, RADIUS], [-RADIUS, RADIUS]
 *  * We first center, then change the scale *<!---->/
 *
 * width = gdk_window_get_width (window);
 * height = gdk_window_get_height (window);
 * radius = MIN (width, height) / 2.;
 *
 * cairo_translate (cr,
 *                  radius + (width - 2 * radius) / 2,
 *                  radius + (height - 2 * radius) / 2);
 *                  cairo_scale (cr, radius / RADIUS, radius / RADIUS);
 *
 * /<!---->* Create a PangoLayout, set the font and text *<!---->/
 * context = gdk_pango_context_get_for_screen (screen);
 * layout = pango_layout_new (context);
 * pango_layout_set_text (layout, "Text", -1);
 * desc = pango_font_description_from_string (FONT);
 * pango_layout_set_font_description (layout, desc);
 * pango_font_description_free (desc);
 *
 * /<!---->* Draw the layout N_WORDS times in a circle *<!---->/
 * for (i = 0; i < N_WORDS; i++)
 *   {
 *     double red, green, blue;
 *     double angle = 2 * G_PI * i / n_words;
 *
 *     cairo_save (cr);
 *
 *     /<!---->* Gradient from red at angle == 60 to blue at angle == 300 *<!---->/
 *     red = (1 + cos (angle - 60)) / 2;
 *     green = 0;
 *     blue = 1 - red;
 *
 *     cairo_set_source_rgb (cr, red, green, blue);
 *     cairo_rotate (cr, angle);
 *
 *     /<!---->* Inform Pango to re-layout the text with the new transformation matrix *<!---->/
 *     pango_cairo_update_layout (cr, layout);
 *
 *     pango_layout_get_size (layout, &width, &height);
 *
 *     cairo_move_to (cr, - width / 2 / PANGO_SCALE, - DEFAULT_TEXT_RADIUS);
 *     pango_cairo_show_layout (cr, layout);
 *
 *     cairo_restore (cr);
 *   }
 *
 * g_object_unref (layout);
 * g_object_unref (context);
 * </programlisting>
 * </example>
 * <figure>
 *   <title>Output of <xref linkend="rotated-example"/></title>
 *   <graphic fileref="rotated-text.png" format="PNG"/>
 * </figure>
 */

127 128 129 130 131
/* Get a clip region to draw only part of a layout. index_ranges
 * contains alternating range starts/stops. The region is the
 * region which contains the given ranges, i.e. if you draw with the
 * region as clip, only the given ranges are drawn.
 */
132
static cairo_region_t*
133 134 135
layout_iter_get_line_clip_region (PangoLayoutIter *iter,
				  gint             x_origin,
				  gint             y_origin,
136
				  const gint      *index_ranges,
137 138 139
				  gint             n_ranges)
{
  PangoLayoutLine *line;
140
  cairo_region_t *clip_region;
141 142 143 144
  PangoRectangle logical_rect;
  gint baseline;
  gint i;

145
  line = pango_layout_iter_get_line_readonly (iter);
146

147
  clip_region = cairo_region_create ();
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

  pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
  baseline = pango_layout_iter_get_baseline (iter);

  i = 0;
  while (i < n_ranges)
    {  
      gint *pixel_ranges = NULL;
      gint n_pixel_ranges = 0;
      gint j;

      /* Note that get_x_ranges returns layout coordinates
       */
      if (index_ranges[i*2+1] >= line->start_index &&
	  index_ranges[i*2] < line->start_index + line->length)
	pango_layout_line_get_x_ranges (line,
					index_ranges[i*2],
					index_ranges[i*2+1],
					&pixel_ranges, &n_pixel_ranges);
  
      for (j = 0; j < n_pixel_ranges; j++)
        {
          GdkRectangle rect;
	  int x_off, y_off;
          
          x_off = PANGO_PIXELS (pixel_ranges[2*j] - logical_rect.x);
	  y_off = PANGO_PIXELS (baseline - logical_rect.y);

          rect.x = x_origin + x_off;
          rect.y = y_origin - y_off;
          rect.width = PANGO_PIXELS (pixel_ranges[2*j + 1] - logical_rect.x) - x_off;
          rect.height = PANGO_PIXELS (baseline - logical_rect.y + logical_rect.height) - y_off;

181
          cairo_region_union_rectangle (clip_region, &rect);
182 183 184 185 186 187 188
        }

      g_free (pixel_ranges);
      ++i;
    }
  return clip_region;
}
189 190

/**
191
 * gdk_pango_layout_line_get_clip_region: (skip)
192 193 194
 * @line: a #PangoLayoutLine 
 * @x_origin: X pixel where you intend to draw the layout line with this clip
 * @y_origin: baseline pixel where you intend to draw the layout line with this clip
195 196 197
 * @index_ranges: (array): array of byte indexes into the layout,
 *     where even members of array are start indexes and odd elements
 *     are end indexes
198 199
 * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
 * 
200
 * Obtains a clip region which contains the areas where the given
201 202
 * ranges of text would be drawn. @x_origin and @y_origin are the top left
 * position of the layout. @index_ranges
203 204 205 206 207 208
 * should contain ranges of bytes in the layout's text. The clip
 * region will include space to the left or right of the line (to the
 * layout bounding box) if you have indexes above or below the indexes
 * contained inside the line. This is to draw the selection all the way
 * to the side of the layout. However, the clip region is in line coordinates,
 * not layout coordinates.
209 210 211 212 213
 *
 * Note that the regions returned correspond to logical extents of the text
 * ranges, not ink extents. So the drawn line may in fact touch areas out of
 * the clip region.  The clip region is mainly useful for highlightling parts
 * of text, such as when text is selected.
214 215 216
 * 
 * Return value: a clip region containing the given ranges
 **/
217
cairo_region_t*
218 219 220
gdk_pango_layout_line_get_clip_region (PangoLayoutLine *line,
                                       gint             x_origin,
                                       gint             y_origin,
221
                                       const gint      *index_ranges,
222 223
                                       gint             n_ranges)
{
224
  cairo_region_t *clip_region;
225
  PangoLayoutIter *iter;
226 227 228 229
  
  g_return_val_if_fail (line != NULL, NULL);
  g_return_val_if_fail (index_ranges != NULL, NULL);
  
230
  iter = pango_layout_get_iter (line->layout);
231
  while (pango_layout_iter_get_line_readonly (iter) != line)
232 233
    pango_layout_iter_next_line (iter);
  
234
  clip_region = layout_iter_get_line_clip_region(iter, x_origin, y_origin, index_ranges, n_ranges);
235

236
  pango_layout_iter_free (iter);
237 238 239 240 241

  return clip_region;
}

/**
242
 * gdk_pango_layout_get_clip_region: (skip)
243 244 245 246 247 248 249
 * @layout: a #PangoLayout 
 * @x_origin: X pixel where you intend to draw the layout with this clip
 * @y_origin: Y pixel where you intend to draw the layout with this clip
 * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
 * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
 * 
 * Obtains a clip region which contains the areas where the given ranges
250 251
 * of text would be drawn. @x_origin and @y_origin are the top left point
 * to center the layout. @index_ranges should contain
252 253
 * ranges of bytes in the layout's text.
 * 
254
 * Note that the regions returned correspond to logical extents of the text
Behdad Esfahbod's avatar
Behdad Esfahbod committed
255
 * ranges, not ink extents. So the drawn layout may in fact touch areas out of
256 257 258
 * the clip region.  The clip region is mainly useful for highlightling parts
 * of text, such as when text is selected.
 * 
259 260
 * Return value: a clip region containing the given ranges
 **/
261
cairo_region_t*
262 263 264
gdk_pango_layout_get_clip_region (PangoLayout *layout,
                                  gint         x_origin,
                                  gint         y_origin,
265
                                  const gint  *index_ranges,
266 267 268
                                  gint         n_ranges)
{
  PangoLayoutIter *iter;  
269
  cairo_region_t *clip_region;
270 271 272 273
  
  g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
  g_return_val_if_fail (index_ranges != NULL, NULL);
  
274
  clip_region = cairo_region_create ();
275 276 277 278 279 280
  
  iter = pango_layout_get_iter (layout);
  
  do
    {
      PangoRectangle logical_rect;
281
      cairo_region_t *line_region;
282 283 284 285 286
      gint baseline;
      
      pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
      baseline = pango_layout_iter_get_baseline (iter);      

287
      line_region = layout_iter_get_line_clip_region(iter, 
288 289
						     x_origin + PANGO_PIXELS (logical_rect.x),
						     y_origin + PANGO_PIXELS (baseline),
290 291
						     index_ranges,
						     n_ranges);
292

293 294
      cairo_region_union (clip_region, line_region);
      cairo_region_destroy (line_region);
295 296 297 298 299 300 301
    }
  while (pango_layout_iter_next_line (iter));

  pango_layout_iter_free (iter);

  return clip_region;
}
302 303 304 305 306 307 308 309 310 311 312 313

/**
 * gdk_pango_context_get:
 * 
 * Creates a #PangoContext for the default GDK screen.
 *
 * The context must be freed when you're finished with it.
 * 
 * When using GTK+, normally you should use gtk_widget_get_pango_context()
 * instead of this function, to get the appropriate context for
 * the widget you intend to render text onto.
 * 
314 315 316 317 318 319
 * The newly created context will have the default font options (see
 * #cairo_font_options_t) for the default screen; if these options
 * change it will not be updated. Using gtk_widget_get_pango_context()
 * is more convenient if you want to keep a context around and track
 * changes to the screen's font rendering settings.
 *
320
 * Return value: (transfer full): a new #PangoContext for the default display
321 322 323 324
 **/
PangoContext *
gdk_pango_context_get (void)
{
Owen Taylor's avatar
Owen Taylor committed
325
  return gdk_pango_context_get_for_screen (gdk_screen_get_default ());
326
}
327

328 329 330 331 332 333 334 335 336 337 338 339
/**
 * gdk_pango_context_get_for_screen:
 * @screen: the #GdkScreen for which the context is to be created.
 * 
 * Creates a #PangoContext for @screen.
 *
 * The context must be freed when you're finished with it.
 * 
 * When using GTK+, normally you should use gtk_widget_get_pango_context()
 * instead of this function, to get the appropriate context for
 * the widget you intend to render text onto.
 * 
340 341 342 343 344 345
 * The newly created context will have the default font options
 * (see #cairo_font_options_t) for the screen; if these options
 * change it will not be updated. Using gtk_widget_get_pango_context()
 * is more convenient if you want to keep a context around and track
 * changes to the screen's font rendering settings.
 * 
346
 * Return value: (transfer full): a new #PangoContext for @screen
347 348 349 350 351 352 353
 *
 * Since: 2.2
 **/
PangoContext *
gdk_pango_context_get_for_screen (GdkScreen *screen)
{
  PangoFontMap *fontmap;
354 355
  PangoContext *context;
  const cairo_font_options_t *options;
356
  double dpi;
357

358 359
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);

360
  fontmap = pango_cairo_font_map_get_default ();
361
  context = pango_font_map_create_context (fontmap);
362

363
  options = gdk_screen_get_font_options (screen);
364 365
  pango_cairo_context_set_font_options (context, options);

366 367 368
  dpi = gdk_screen_get_resolution (screen);
  pango_cairo_context_set_resolution (context, dpi);

369
  return context;
370
}