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