]> Pileus Git - ~andy/gtk/blob - gdk/gdkpango.c
Add a missing pango_layout_iter_free ().
[~andy/gtk] / gdk / gdkpango.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "gdkcolor.h"
21 #include "gdkgc.h"
22 #include "gdkpango.h"
23 #include "gdkrgb.h"
24 #include "gdkprivate.h"
25
26 #define GDK_INFO_KEY "gdk-info"
27
28 typedef struct _GdkPangoContextInfo GdkPangoContextInfo;
29
30 struct _GdkPangoContextInfo
31 {
32   GdkColormap *colormap;
33 };
34
35 static PangoAttrType gdk_pango_attr_stipple_type;
36 static PangoAttrType gdk_pango_attr_embossed_type;
37
38 static void gdk_pango_get_item_properties (PangoItem      *item,
39                                            PangoUnderline *uline,
40                                            gboolean       *strikethrough,
41                                            gint           *rise,
42                                            PangoColor     *fg_color,
43                                            gboolean       *fg_set,
44                                            PangoColor     *bg_color,
45                                            gboolean       *bg_set,
46                                            gboolean       *embossed,
47                                            GdkBitmap     **stipple,
48                                            gboolean       *shape_set,
49                                            PangoRectangle *ink_rect,
50                                            PangoRectangle *logical_rect);
51
52 static void
53 gdk_pango_context_destroy (GdkPangoContextInfo *info)
54 {
55   if (info->colormap)
56     gdk_colormap_unref (info->colormap);
57   g_free (info);
58 }
59
60 static GdkPangoContextInfo *
61 gdk_pango_context_get_info (PangoContext *context, gboolean create)
62 {
63   GdkPangoContextInfo *info =
64     g_object_get_qdata (G_OBJECT (context),
65                         g_quark_try_string (GDK_INFO_KEY));
66   if (!info && create)
67     {
68       info = g_new (GdkPangoContextInfo, 1);
69       info->colormap = NULL;
70
71       g_object_set_qdata_full (G_OBJECT (context),
72                                g_quark_from_static_string (GDK_INFO_KEY),
73                                info, (GDestroyNotify)gdk_pango_context_destroy);
74     }
75
76   return info;
77 }
78
79 static GdkGC *
80 gdk_pango_get_gc (PangoContext   *context,
81                   PangoColor     *fg_color,
82                   GdkBitmap      *stipple,
83                   GdkGC          *base_gc)
84 {
85   GdkGC *result;
86   GdkPangoContextInfo *info;
87   
88   g_return_val_if_fail (context != NULL, NULL);
89
90   info = gdk_pango_context_get_info (context, FALSE);
91
92   if (info == NULL || info->colormap == NULL)
93     {
94       g_warning ("you must set the colormap on a PangoContext before using it to draw a layout");
95       return NULL;
96     }
97
98   result = gdk_gc_new (gdk_get_default_root_window ());
99   gdk_gc_copy (result, base_gc);
100   
101   if (fg_color)
102     {
103       GdkColor color;
104       
105       color.red = fg_color->red;
106       color.green = fg_color->green;
107       color.blue = fg_color->blue;
108
109       gdk_rgb_find_color (info->colormap, &color);
110       gdk_gc_set_foreground (result, &color);
111     }
112
113   if (stipple)
114     {
115       gdk_gc_set_fill (result, GDK_STIPPLED);
116       gdk_gc_set_stipple (result, stipple);
117     }
118   
119   return result;
120 }
121
122 static void
123 gdk_pango_free_gc (PangoContext *context,
124                    GdkGC        *gc)
125 {
126   gdk_gc_unref (gc);
127 }
128
129 void
130 gdk_pango_context_set_colormap (PangoContext *context,
131                                 GdkColormap  *colormap)
132 {
133   GdkPangoContextInfo *info;
134   
135   g_return_if_fail (context != NULL);
136
137   info = gdk_pango_context_get_info (context, TRUE);
138   g_return_if_fail (info != NULL);
139   
140   if (info->colormap != colormap)
141     {
142       if (info->colormap)
143         gdk_colormap_unref (info->colormap);
144
145       info->colormap = colormap;
146       
147       if (info->colormap)
148         gdk_colormap_ref (info->colormap);
149     }
150 }
151
152 /**
153  * gdk_draw_layout_line_with_colors:
154  * @drawable:  the drawable on which to draw the line
155  * @gc:        base graphics to use
156  * @x:         the x position of start of string (in pixels)
157  * @y:         the y position of baseline (in pixels)
158  * @line:      a #PangoLayoutLine
159  * @foreground: foreground override color, or %NULL for none
160  * @background: background override color, or %NULL for none
161  *
162  * Render a #PangoLayoutLine onto a #GdkDrawable, overriding the
163  * layout's normal colors with @foreground and/or @background.
164  * @foreground and @background need not be allocated.
165  */
166 void 
167 gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
168                                   GdkGC            *gc,
169                                   gint              x, 
170                                   gint              y,
171                                   PangoLayoutLine  *line,
172                                   GdkColor         *foreground,
173                                   GdkColor         *background)
174 {
175   GSList *tmp_list = line->runs;
176   PangoRectangle overall_rect;
177   PangoRectangle logical_rect;
178   PangoRectangle ink_rect;
179   PangoContext *context;
180   gint x_off = 0;
181   gint rise = 0;
182   gboolean embossed;
183   GdkBitmap *stipple;
184
185   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
186   g_return_if_fail (GDK_IS_GC (gc));
187   g_return_if_fail (line != NULL);
188
189   context = pango_layout_get_context (line->layout);
190   
191   pango_layout_line_get_extents (line,NULL, &overall_rect);
192   
193   while (tmp_list)
194     {
195       PangoUnderline uline = PANGO_UNDERLINE_NONE;
196       PangoLayoutRun *run = tmp_list->data;
197       PangoColor fg_color, bg_color;
198       gboolean strike, fg_set, bg_set, shape_set;
199       GdkGC *fg_gc;
200       gint risen_y;
201       
202       tmp_list = tmp_list->next;
203       
204       gdk_pango_get_item_properties (run->item, &uline,
205                                      &strike,
206                                      &rise,
207                                      &fg_color, &fg_set,
208                                      &bg_color, &bg_set,
209                                      &embossed,
210                                      &stipple,
211                                      &shape_set,
212                                      &ink_rect,
213                                      &logical_rect);
214
215       /* we subtract the rise because X coordinates are upside down */
216       risen_y = y - rise / PANGO_SCALE;
217       
218       if (!shape_set)
219         {
220           if (uline == PANGO_UNDERLINE_NONE)
221             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
222                                         NULL, &logical_rect);
223           else
224             pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
225                                         &ink_rect, &logical_rect);
226         }
227
228       if (bg_set || background)
229         {
230           GdkGC *bg_gc;
231           PangoColor tmp = bg_color;
232           
233           if (background)
234             {
235               tmp.red = background->red;
236               tmp.blue = background->blue;
237               tmp.green = background->green;
238             }
239           
240           bg_gc = gdk_pango_get_gc (context, &tmp, stipple, gc);
241           
242           gdk_draw_rectangle (drawable, bg_gc, TRUE,
243                               x + (x_off + logical_rect.x) / PANGO_SCALE,
244                               risen_y + overall_rect.y / PANGO_SCALE,
245                               logical_rect.width / PANGO_SCALE,
246                               overall_rect.height / PANGO_SCALE);
247
248           if (stipple)
249             gdk_gc_set_fill (bg_gc, GDK_SOLID);
250           
251           gdk_pango_free_gc (context, bg_gc);
252         }
253
254       if (fg_set || stipple || foreground)
255         {
256           PangoColor tmp = fg_color;
257           
258           if (foreground)
259             {
260               tmp.red = foreground->red;
261               tmp.blue = foreground->blue;
262               tmp.green = foreground->green;
263             }
264           
265           fg_gc = gdk_pango_get_gc (context, fg_set ? &tmp : NULL,
266                                     stipple, gc);
267         }
268       else
269         fg_gc = gc;
270       
271       if (!shape_set)
272         {
273           gint gx, gy;
274
275           gx = x + x_off / PANGO_SCALE;
276           gy = risen_y;
277           
278           if (embossed)
279             {
280               PangoColor color = { 65535, 65535, 65535 };
281               GdkGC *white_gc = gdk_pango_get_gc (context, &color, stipple, fg_gc);
282               gdk_draw_glyphs (drawable, white_gc, run->item->analysis.font,
283                                gx + 1,
284                                gy + 1,
285                                run->glyphs);
286               gdk_pango_free_gc (context, white_gc);
287             }
288           
289           gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
290                            gx, gy,
291                            run->glyphs);
292         }
293       
294       switch (uline)
295         {
296         case PANGO_UNDERLINE_NONE:
297           break;
298         case PANGO_UNDERLINE_DOUBLE:
299           gdk_draw_line (drawable, fg_gc,
300                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
301                          risen_y + 3,
302                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
303                          risen_y + 3);
304           /* Fall through */
305         case PANGO_UNDERLINE_SINGLE:
306           gdk_draw_line (drawable, fg_gc,
307                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
308                          risen_y + 1,
309                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
310                          risen_y + 1);
311           break;
312         case PANGO_UNDERLINE_LOW:
313           gdk_draw_line (drawable, fg_gc,
314                          x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
315                          risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1,
316                          x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
317                          risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1);
318           break;
319         }
320
321       if (strike)
322       {
323           int centerline = logical_rect.y + logical_rect.height / 2;
324           
325           gdk_draw_line (drawable, fg_gc,
326                          x + (x_off + logical_rect.x) / PANGO_SCALE - 1,
327                          risen_y + centerline / PANGO_SCALE,
328                          x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE + 1,
329                          risen_y + centerline / PANGO_SCALE);
330       }
331       
332       if (fg_gc != gc)
333         gdk_pango_free_gc (context, fg_gc);
334
335       x_off += logical_rect.width;
336     }
337 }
338
339 /**
340  * gdk_draw_layout_with_colors:
341  * @drawable:  the drawable on which to draw string
342  * @gc:        base graphics context to use
343  * @x:         the X position of the left of the layout (in pixels)
344  * @y:         the Y position of the top of the layout (in pixels)
345  * @layout:    a #PangoLayout
346  * @foreground: foreground override color, or %NULL for none
347  * @background: background override color, or %NULL for none
348  *
349  * Render a #PangoLayout onto a #GdkDrawable, overriding the
350  * layout's normal colors with @foreground and/or @background.
351  * @foreground and @background need not be allocated.
352  */
353 void 
354 gdk_draw_layout_with_colors (GdkDrawable     *drawable,
355                              GdkGC           *gc,
356                              int              x, 
357                              int              y,
358                              PangoLayout     *layout,
359                              GdkColor        *foreground,
360                              GdkColor        *background)
361 {
362   PangoLayoutIter *iter;
363   
364   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
365   g_return_if_fail (GDK_IS_GC (gc));
366   g_return_if_fail (PANGO_IS_LAYOUT (layout));
367
368   iter = pango_layout_get_iter (layout);
369   
370   do
371     {
372       PangoRectangle logical_rect;
373       PangoLayoutLine *line;
374       int baseline;
375       
376       line = pango_layout_iter_get_line (iter);
377       
378       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
379       baseline = pango_layout_iter_get_baseline (iter);
380       
381       gdk_draw_layout_line_with_colors (drawable, gc,
382                                         x + logical_rect.x / PANGO_SCALE,
383                                         y + baseline / PANGO_SCALE,
384                                         line,
385                                         foreground,
386                                         background);
387     }
388   while (pango_layout_iter_next_line (iter));
389
390   pango_layout_iter_free (iter);
391 }
392
393 /**
394  * gdk_draw_layout_line:
395  * @drawable:  the drawable on which to draw the line
396  * @gc:        base graphics to use
397  * @x:         the x position of start of string (in pixels)
398  * @y:         the y position of baseline (in pixels)
399  * @line:      a #PangoLayoutLine
400  *
401  * Render a #PangoLayoutLine onto an GDK drawable
402  */
403 void 
404 gdk_draw_layout_line (GdkDrawable      *drawable,
405                       GdkGC            *gc,
406                       gint              x, 
407                       gint              y,
408                       PangoLayoutLine  *line)
409 {
410   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
411   g_return_if_fail (GDK_IS_GC (gc));
412   g_return_if_fail (line != NULL);
413   
414   gdk_draw_layout_line_with_colors (drawable, gc, x, y, line, NULL, NULL);
415 }
416
417 /**
418  * gdk_draw_layout:
419  * @drawable:  the drawable on which to draw string
420  * @gc:        base graphics context to use
421  * @x:         the X position of the left of the layout (in pixels)
422  * @y:         the Y position of the top of the layout (in pixels)
423  * @layout:    a #PangoLayout
424  *
425  * Render a #PangoLayout onto a GDK drawable
426  */
427 void 
428 gdk_draw_layout (GdkDrawable     *drawable,
429                  GdkGC           *gc,
430                  int              x, 
431                  int              y,
432                  PangoLayout     *layout)
433 {
434   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
435   g_return_if_fail (GDK_IS_GC (gc));
436   g_return_if_fail (PANGO_IS_LAYOUT (layout));
437
438   gdk_draw_layout_with_colors (drawable, gc, x, y, layout, NULL, NULL);
439 }
440
441 static void
442 gdk_pango_get_item_properties (PangoItem      *item,
443                                PangoUnderline *uline,
444                                gboolean       *strikethrough,
445                                gint           *rise,
446                                PangoColor     *fg_color,
447                                gboolean       *fg_set,
448                                PangoColor     *bg_color,
449                                gboolean       *bg_set,
450                                gboolean       *embossed,
451                                GdkBitmap     **stipple,
452                                gboolean       *shape_set,
453                                PangoRectangle *ink_rect,
454                                PangoRectangle *logical_rect)
455 {
456   GSList *tmp_list = item->analysis.extra_attrs;
457
458   if (strikethrough)
459       *strikethrough = FALSE;
460   
461   if (fg_set)
462     *fg_set = FALSE;
463   
464   if (bg_set)
465     *bg_set = FALSE;
466
467   if (shape_set)
468     *shape_set = FALSE;
469
470   if (rise)
471     *rise = 0;
472
473   if (embossed)
474     *embossed = FALSE;
475
476   if (stipple)
477     *stipple = NULL;
478   
479   while (tmp_list)
480     {
481       PangoAttribute *attr = tmp_list->data;
482
483       switch (attr->klass->type)
484         {
485         case PANGO_ATTR_UNDERLINE:
486           if (uline)
487             *uline = ((PangoAttrInt *)attr)->value;
488           break;
489
490         case PANGO_ATTR_STRIKETHROUGH:
491             if (strikethrough)
492                 *strikethrough = ((PangoAttrInt *)attr)->value;
493             break;
494             
495         case PANGO_ATTR_FOREGROUND:
496           if (fg_color)
497             *fg_color = ((PangoAttrColor *)attr)->color;
498           if (fg_set)
499             *fg_set = TRUE;
500           
501           break;
502           
503         case PANGO_ATTR_BACKGROUND:
504           if (bg_color)
505             *bg_color = ((PangoAttrColor *)attr)->color;
506           if (bg_set)
507             *bg_set = TRUE;
508           
509           break;
510
511         case PANGO_ATTR_SHAPE:
512           if (shape_set)
513             *shape_set = TRUE;
514           if (logical_rect)
515             *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
516           if (ink_rect)
517             *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
518           break;
519
520         case PANGO_ATTR_RISE:
521           if (rise)
522             *rise = ((PangoAttrInt *)attr)->value;
523           break;
524           
525         default:
526           /* stipple_type and embossed_type aren't necessarily
527            * initialized, but they are 0, which is an
528            * invalid type so won't occur. 
529            */
530           if (stipple && attr->klass->type == gdk_pango_attr_stipple_type)
531             {
532               *stipple = ((GdkPangoAttrStipple*)attr)->stipple;
533             }
534           else if (embossed && attr->klass->type == gdk_pango_attr_embossed_type)
535             {
536               *embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
537             }
538           break;
539         }
540       tmp_list = tmp_list->next;
541     }
542 }
543
544
545 static PangoAttribute *
546 gdk_pango_attr_stipple_copy (const PangoAttribute *attr)
547 {
548   const GdkPangoAttrStipple *src = (const GdkPangoAttrStipple*) attr;
549
550   return gdk_pango_attr_stipple_new (src->stipple);
551 }
552
553 static void
554 gdk_pango_attr_stipple_destroy (PangoAttribute *attr)
555 {
556   GdkPangoAttrStipple *st = (GdkPangoAttrStipple*) attr;
557
558   if (st->stipple)
559     g_object_unref (G_OBJECT (st->stipple));
560   
561   g_free (attr);
562 }
563
564 static gboolean
565 gdk_pango_attr_stipple_compare (const PangoAttribute *attr1,
566                                     const PangoAttribute *attr2)
567 {
568   const GdkPangoAttrStipple *a = (const GdkPangoAttrStipple*) attr1;
569   const GdkPangoAttrStipple *b = (const GdkPangoAttrStipple*) attr2;
570
571   return a->stipple == b->stipple;
572 }
573
574 /**
575  * gdk_pango_attr_stipple_new:
576  * @stipple: a bitmap to be set as stipple
577  *
578  * Creates a new attribute containing a stipple bitmap to be used when
579  * rendering the text.
580  *
581  * Return value: new #PangoAttribute
582  **/
583
584 PangoAttribute *
585 gdk_pango_attr_stipple_new (GdkBitmap *stipple)
586 {
587   GdkPangoAttrStipple *result;
588   
589   static PangoAttrClass klass = {
590     0,
591     gdk_pango_attr_stipple_copy,
592     gdk_pango_attr_stipple_destroy,
593     gdk_pango_attr_stipple_compare
594   };
595
596   if (!klass.type)
597     klass.type = gdk_pango_attr_stipple_type =
598       pango_attr_type_register ("GdkPangoAttrStipple");
599
600   result = g_new (GdkPangoAttrStipple, 1);
601   result->attr.klass = &klass;
602
603   if (stipple)
604     g_object_ref (stipple);
605   
606   result->stipple = stipple;
607
608   return (PangoAttribute *)result;
609 }
610
611 static PangoAttribute *
612 gdk_pango_attr_embossed_copy (const PangoAttribute *attr)
613 {
614   const GdkPangoAttrEmbossed *e = (const GdkPangoAttrEmbossed*) attr;
615
616   return gdk_pango_attr_embossed_new (e->embossed);
617 }
618
619 static void
620 gdk_pango_attr_embossed_destroy (PangoAttribute *attr)
621 {
622   g_free (attr);
623 }
624
625 static gboolean
626 gdk_pango_attr_embossed_compare (const PangoAttribute *attr1,
627                                  const PangoAttribute *attr2)
628 {
629   const GdkPangoAttrEmbossed *e1 = (const GdkPangoAttrEmbossed*) attr1;
630   const GdkPangoAttrEmbossed *e2 = (const GdkPangoAttrEmbossed*) attr2;
631
632   return e1->embossed == e2->embossed;
633 }
634
635 /**
636  * gdk_pango_attr_embossed_new:
637  * @embossed: a bitmap to be set as embossed
638  *
639  * Creates a new attribute containing a embossed bitmap to be used when
640  * rendering the text.
641  *
642  * Return value: new #PangoAttribute
643  **/
644
645 PangoAttribute *
646 gdk_pango_attr_embossed_new (gboolean embossed)
647 {
648   GdkPangoAttrEmbossed *result;
649   
650   static PangoAttrClass klass = {
651     0,
652     gdk_pango_attr_embossed_copy,
653     gdk_pango_attr_embossed_destroy,
654     gdk_pango_attr_embossed_compare
655   };
656
657   if (!klass.type)
658     klass.type = gdk_pango_attr_embossed_type =
659       pango_attr_type_register ("GdkPangoAttrEmbossed");
660
661   result = g_new (GdkPangoAttrEmbossed, 1);
662   result->attr.klass = &klass;
663   result->embossed = embossed;
664   
665   return (PangoAttribute *)result;
666 }
667
668 /* Get a clip region to draw only part of a layout. index_ranges
669  * contains alternating range starts/stops. The region is the
670  * region which contains the given ranges, i.e. if you draw with the
671  * region as clip, only the given ranges are drawn.
672  */
673
674 /**
675  * gdk_pango_layout_line_get_clip_region:
676  * @line: a #PangoLayoutLine 
677  * @x_origin: X pixel where you intend to draw the layout line with this clip
678  * @y_origin: baseline pixel where you intend to draw the layout line with this clip
679  * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
680  * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
681  * 
682  * Obtains a clip region which contains the areas where the given
683  * ranges of text would be drawn. @x_origin and @y_origin are the same
684  * position you would pass to gdk_draw_layout_line(). @index_ranges
685  * should contain ranges of bytes in the layout's text. The clip
686  * region will include space to the left or right of the line (to the
687  * layout bounding box) if you have indexes above or below the indexes
688  * contained inside the line. This is to draw the selection all the way
689  * to the side of the layout. However, the clip region is in line coordinates,
690  * not layout coordinates.
691  * 
692  * Return value: a clip region containing the given ranges
693  **/
694 GdkRegion*
695 gdk_pango_layout_line_get_clip_region (PangoLayoutLine *line,
696                                        gint             x_origin,
697                                        gint             y_origin,
698                                        gint            *index_ranges,
699                                        gint             n_ranges)
700 {
701   GdkRegion *clip_region;
702   gint i;
703   PangoRectangle logical_rect;
704   PangoLayoutIter *iter;
705   gint baseline;
706   
707   g_return_val_if_fail (line != NULL, NULL);
708   g_return_val_if_fail (index_ranges != NULL, NULL);
709   
710   clip_region = gdk_region_new ();
711
712   iter = pango_layout_get_iter (line->layout);
713   while (pango_layout_iter_get_line (iter) != line)
714     pango_layout_iter_next_line (iter);
715   
716   pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
717   baseline = pango_layout_iter_get_baseline (iter);
718   
719   pango_layout_iter_free (iter);
720   
721   i = 0;
722   while (i < n_ranges)
723     {  
724       gint *pixel_ranges = NULL;
725       gint n_pixel_ranges = 0;
726       gint j;
727
728       /* Note that get_x_ranges returns layout coordinates
729        */
730       pango_layout_line_get_x_ranges (line,
731                                       index_ranges[i*2],
732                                       index_ranges[i*2+1],
733                                       &pixel_ranges, &n_pixel_ranges);
734   
735       for (j=0; j < n_pixel_ranges; j++)
736         {
737           GdkRectangle rect;
738           
739           rect.x = x_origin + pixel_ranges[2*j] / PANGO_SCALE - logical_rect.x / PANGO_SCALE;
740           rect.y = y_origin - (baseline / PANGO_SCALE - logical_rect.y / PANGO_SCALE);
741           rect.width = (pixel_ranges[2*j + 1] - pixel_ranges[2*j]) / PANGO_SCALE;
742           rect.height = logical_rect.height / PANGO_SCALE;
743           
744           gdk_region_union_with_rect (clip_region, &rect);
745         }
746
747       g_free (pixel_ranges);
748       ++i;
749     }
750
751   return clip_region;
752 }
753
754 /**
755  * gdk_pango_layout_get_clip_region:
756  * @layout: a #PangoLayout 
757  * @x_origin: X pixel where you intend to draw the layout with this clip
758  * @y_origin: Y pixel where you intend to draw the layout with this clip
759  * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes
760  * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges
761  * 
762  * Obtains a clip region which contains the areas where the given ranges
763  * of text would be drawn. @x_origin and @y_origin are the same position
764  * you would pass to gdk_draw_layout_line(). @index_ranges should contain
765  * ranges of bytes in the layout's text.
766  * 
767  * Return value: a clip region containing the given ranges
768  **/
769 GdkRegion*
770 gdk_pango_layout_get_clip_region (PangoLayout *layout,
771                                   gint         x_origin,
772                                   gint         y_origin,
773                                   gint        *index_ranges,
774                                   gint         n_ranges)
775 {
776   PangoLayoutIter *iter;  
777   GdkRegion *clip_region;
778   
779   g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL);
780   g_return_val_if_fail (index_ranges != NULL, NULL);
781   
782   clip_region = gdk_region_new ();
783   
784   iter = pango_layout_get_iter (layout);
785   
786   do
787     {
788       PangoRectangle logical_rect;
789       PangoLayoutLine *line;
790       GdkRegion *line_region;
791       gint baseline;
792       
793       line = pango_layout_iter_get_line (iter);      
794
795       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
796       baseline = pango_layout_iter_get_baseline (iter);      
797
798       line_region = gdk_pango_layout_line_get_clip_region (line,
799                                                            x_origin + logical_rect.x / PANGO_SCALE,
800                                                            y_origin + baseline / PANGO_SCALE,
801                                                            index_ranges,
802                                                            n_ranges);
803
804       gdk_region_union (clip_region, line_region);
805       gdk_region_destroy (line_region);
806     }
807   while (pango_layout_iter_next_line (iter));
808
809   pango_layout_iter_free (iter);
810
811   return clip_region;
812 }