]> Pileus Git - ~andy/gtk/blobdiff - gdk/gdkpango.c
Add GdkPangoRenderer, a subclass of PangoRenderer targeting GDK drawables.
[~andy/gtk] / gdk / gdkpango.c
index 93e5ab2883f496870f4085c12d85902412822745..374d90d6d1d9ff5273d02811935be06adf17b102 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <config.h>
+#include <math.h>
 #include "gdkalias.h"
 #include "gdkcolor.h"
 #include "gdkgc.h"
 #include "gdkprivate.h"
 #include "gdkscreen.h"
 
+/* This is for P_() ... a bit non-kosher, but works fine */
+#include "gtk/gtkintl.h"
+
 #define GDK_INFO_KEY "gdk-info"
 
-typedef struct _GdkPangoContextInfo GdkPangoContextInfo;
+/* We have various arrays indexed by render part; if PangoRenderPart
+ * is extended, we want to make sure not to overwrite the end of
+ * those arrays.
+ */
+#define MAX_RENDER_PART  PANGO_RENDER_PART_STRIKETHROUGH
 
-struct _GdkPangoContextInfo
+struct _GdkPangoRendererPrivate
 {
-  GdkColormap *colormap;
+  GdkScreen *screen;
+
+  /* GdkPangoRenderer specific state */
+  PangoColor override_color[MAX_RENDER_PART + 1];
+  gboolean override_color_set[MAX_RENDER_PART + 1];
+  
+  GdkBitmap *stipple[MAX_RENDER_PART + 1];
+  gboolean embossed;
+
+  /* When switching between the normal and shadow copies when
+   * drawing shadows we can get unexpected recursion into the
+   * drawing functions; the 'in_emboss' flag guards against that.
+   */
+  gboolean in_emboss;
+
+  /* Current target */
+  GdkDrawable *drawable;
+  GdkGC *base_gc;
+
+  /* Cached GC, derived from base_gc */
+  GdkGC *gc;
+  PangoColor gc_color;
+  gboolean gc_color_set;
+  GdkBitmap *gc_stipple;
+  
+  /* we accumulate trapezoids for the same PangoRenderPart */
+  GArray *trapezoids;
+  PangoRenderPart trapezoid_part;
 };
 
 static PangoAttrType gdk_pango_attr_stipple_type;
 static PangoAttrType gdk_pango_attr_embossed_type;
 
-static void gdk_pango_get_item_properties (PangoItem      *item,
-                                          PangoUnderline *uline,
-                                          gboolean       *strikethrough,
-                                           gint           *rise,
-                                          PangoColor     *fg_color,
-                                          gboolean       *fg_set,
-                                          PangoColor     *bg_color,
-                                          gboolean       *bg_set,
-                                           gboolean       *embossed,
-                                           GdkBitmap     **stipple,
-                                          gboolean       *shape_set,
-                                          PangoRectangle *ink_rect,
-                                          PangoRectangle *logical_rect);
+static void flush_trapezoids (GdkPangoRenderer *gdk_renderer);
+
+enum {
+  PROP_0,
+  PROP_SCREEN
+};
+
+G_DEFINE_TYPE (GdkPangoRenderer, gdk_pango_renderer, PANGO_TYPE_RENDERER)
 
 static void
-gdk_pango_context_destroy (GdkPangoContextInfo *info)
+gdk_pango_renderer_finalize (GObject *object)
 {
-  if (info->colormap)
-    g_object_unref (info->colormap);
-  g_free (info);
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+  int i;
+
+  if (priv->gc)
+    g_object_unref (priv->gc);
+  if (priv->gc_stipple)
+    g_object_unref (priv->gc_stipple);
+  if (priv->base_gc)
+    g_object_unref (priv->base_gc);
+  if (priv->drawable)
+    g_object_unref (priv->drawable);
+
+  for (i = 0; i <= MAX_RENDER_PART; i++)
+    if (priv->stipple[i])
+      g_object_unref (priv->stipple[i]);
+
+  g_array_free (priv->trapezoids, TRUE);
+
+  G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->finalize (object);
 }
 
-static GdkPangoContextInfo *
-gdk_pango_context_get_info (PangoContext *context, gboolean create)
+static GObject*
+gdk_pango_renderer_constructor (GType                  type,
+                               guint                  n_construct_properties,
+                               GObjectConstructParam *construct_params)
 {
-  GdkPangoContextInfo *info =
-    g_object_get_qdata (G_OBJECT (context),
-                        g_quark_try_string (GDK_INFO_KEY));
-  if (!info && create)
+  GObject *object;
+  GdkPangoRenderer *gdk_renderer;
+
+  object = (* G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->constructor) (type,
+                                                                             n_construct_properties,
+                                                                             construct_params);
+
+  gdk_renderer = GDK_PANGO_RENDERER (object);
+  
+  if (!gdk_renderer->priv->screen)
     {
-      info = g_new (GdkPangoContextInfo, 1);
-      info->colormap = NULL;
+      g_warning ("Screen must be specified at construct time for GdkPangoRenderer");
+      gdk_renderer->priv->screen = gdk_screen_get_default ();
+    }
 
-      g_object_set_qdata_full (G_OBJECT (context),
-                               g_quark_from_static_string (GDK_INFO_KEY),
-                               info, (GDestroyNotify)gdk_pango_context_destroy);
+  return object;
+}
+
+/* Adjusts matrix and color for the renderer to draw the secondar
+ * "shadow" copy for embossed text */
+static void
+emboss_renderer (PangoRenderer   *renderer,
+                PangoRenderPart  part,
+                PangoMatrix    **save_matrix,
+                PangoColor     **save_color)
+{
+  GdkPangoRendererPrivate *priv = GDK_PANGO_RENDERER(renderer)->priv;
+  static const PangoColor white = { 0xffff, 0xffff, 0xffff };
+  PangoMatrix tmp_matrix = PANGO_MATRIX_INIT;
+
+  priv->in_emboss = TRUE;
+  
+  *save_color = pango_renderer_get_color (renderer, part);
+  if (*save_color)
+    *save_color = pango_color_copy (*save_color);
+  
+  *save_matrix = renderer->matrix;
+  if (*save_matrix)
+    {
+      *save_matrix = pango_matrix_copy (*save_matrix);
+      tmp_matrix = **save_matrix;
     }
+  
+  /* The gymnastics here to adjust the matrix are because we want
+   * to offset by +1,+1 in device-space, not in user-space,
+   * so we can't just draw the layout at x + 1, y + 1
+   */
+  tmp_matrix.x0 += 1;
+  tmp_matrix.y0 += 1;
+  
+  pango_renderer_set_matrix (renderer, &tmp_matrix);
+  pango_renderer_set_color (renderer, part, &white);
+}
 
-  return info;
+/* Restores from emboss_renderer() */
+static void
+unemboss_renderer (PangoRenderer   *renderer,
+                  PangoRenderPart  part,
+                  PangoMatrix    **save_matrix,
+                  PangoColor     **save_color)
+{
+  GdkPangoRendererPrivate *priv = GDK_PANGO_RENDERER(renderer)->priv;
+  pango_renderer_set_matrix (renderer, *save_matrix);
+  pango_renderer_set_color (renderer, part, *save_color);
+
+  if (*save_matrix)
+    pango_matrix_free (*save_matrix);
+  if (*save_color)
+    pango_color_free (*save_color);
+
+  priv->in_emboss = FALSE;
 }
 
+/* Gets the GC for drawing @part. This make involve copying the base GC
+ * for the renderer, in which case we keep a one-GC cache. */
 static GdkGC *
-gdk_pango_get_gc (GdkDrawable    *drawable,
-                 PangoContext   *context,
-                 PangoColor     *fg_color,
-                  GdkBitmap      *stipple,
-                 GdkGC          *base_gc)
+get_gc (GdkPangoRenderer *gdk_renderer,
+       PangoRenderPart   part)
+{
+  PangoRenderer *renderer = PANGO_RENDERER (gdk_renderer);
+  PangoColor *color;
+  GdkBitmap *stipple;
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  color = pango_renderer_get_color (renderer, part);
+
+  if (part <= MAX_RENDER_PART)
+    stipple = priv->stipple[part];
+  else
+    stipple = NULL;
+
+  if (!color && !stipple)      /* nothing override, use base_gc */
+    return priv->base_gc;
+  else
+    {
+      gboolean new_stipple = FALSE;
+      gboolean new_color = FALSE;
+      
+      if (stipple != priv->gc_stipple)
+       new_stipple = TRUE;
+
+      if ((priv->gc_color_set && !color) ||
+         (!priv->gc_color_set && color) ||
+         priv->gc_color.red != color->red ||
+         priv->gc_color.green != color->green ||
+         priv->gc_color.blue != color->blue)
+       new_color = TRUE;
+      
+      if (!priv->gc)
+       {
+         priv->gc = gdk_gc_new (priv->drawable);
+         gdk_gc_copy (priv->gc, priv->base_gc);
+       }
+      else if (new_color && priv->gc_color_set && !color)
+       {
+         /* We have to recopy the original GC onto the cached GC
+          * to get the default color */
+         new_stipple = TRUE;
+         gdk_gc_copy (priv->gc, priv->base_gc);
+       }
+      else if (new_stipple && priv->gc_stipple && !stipple)
+       {
+         /* Similarly, we need to make a new copy to restore to the
+          * default stipple state (the caller may have set a stipple
+          * on the GC, and even if not, gdk_gc_set_stipple (gc, NULL)
+          * doesn't work currently to restore to the default X stipple) */
+         new_color = TRUE;
+         gdk_gc_copy (priv->gc, priv->base_gc);
+       }
+
+      if (new_color)
+       {
+         if (color)
+           {
+             GdkColor gdk_color;
+
+             gdk_color.red = color->red;
+             gdk_color.green = color->green;
+             gdk_color.blue = color->blue;
+             
+             gdk_gc_set_rgb_fg_color (priv->gc, &gdk_color);
+
+             priv->gc_color = *color;
+             priv->gc_color_set = TRUE;
+           }
+         else
+           priv->gc_color_set = FALSE;
+       }
+
+      if (new_stipple)
+       {
+         if (priv->gc_stipple)
+           g_object_unref (priv->gc_stipple);
+
+         if (stipple)
+           {
+             gdk_gc_set_stipple (priv->gc, stipple);
+             gdk_gc_set_fill (priv->gc, GDK_STIPPLED);
+             priv->gc_stipple = g_object_ref (stipple);
+           }
+         else
+           priv->gc_stipple = NULL;
+       }
+
+      return priv->gc;
+    }
+}
+
+static void
+gdk_pango_renderer_draw_glyphs (PangoRenderer    *renderer,
+                               PangoFont        *font,
+                               PangoGlyphString *glyphs,
+                               int               x,
+                               int               y)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  flush_trapezoids (gdk_renderer);
+
+  if (!priv->in_emboss && priv->embossed)
+    {
+      PangoMatrix *save_matrix;
+      PangoColor *save_color;
+
+      emboss_renderer (renderer, PANGO_RENDER_PART_FOREGROUND, &save_matrix, &save_color);
+      gdk_draw_glyphs_transformed (priv->drawable,
+                                  get_gc (gdk_renderer, PANGO_RENDER_PART_FOREGROUND),
+                                  renderer->matrix, font, x, y, glyphs);
+      unemboss_renderer (renderer, PANGO_RENDER_PART_FOREGROUND, &save_matrix, &save_color);
+    }
+
+  gdk_draw_glyphs_transformed (priv->drawable,
+                              get_gc (gdk_renderer, PANGO_RENDER_PART_FOREGROUND),
+                              renderer->matrix, font, x, y, glyphs);
+}
+
+/* Outputs any pending trapezoids, we do this when the part or
+ * part color changes, when we are about to draw text, etc. */
+static void
+flush_trapezoids (GdkPangoRenderer *gdk_renderer)
 {
-  GdkGC *result;
-  GdkPangoContextInfo *info;
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  if (!priv->trapezoids || priv->trapezoids->len == 0)
+    return;
+
+  gdk_draw_trapezoids (priv->drawable,
+                      get_gc (gdk_renderer, priv->trapezoid_part),
+                      (GdkTrapezoid *)priv->trapezoids->data,
+                      priv->trapezoids->len);
+
+  g_array_set_size (priv->trapezoids, 0);
+}
+
+/* Draws a single trapezoid ... we don't draw it immediately, but rather
+ * cache it to join together with other trapezoids that form part of the
+ * same logical shape */
+static void
+gdk_pango_renderer_draw_trapezoid (PangoRenderer   *renderer,
+                                  PangoRenderPart  part,
+                                  double           y1,
+                                  double           x11,
+                                  double           x21,
+                                  double           y2,
+                                  double           x12,
+                                  double           x22)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkTrapezoid trap;
+
+  if (!gdk_renderer->priv->trapezoids)
+    gdk_renderer->priv->trapezoids = g_array_new (FALSE, FALSE,
+                                                 sizeof (GdkTrapezoid));
   
-  g_return_val_if_fail (context != NULL, NULL);
+  if (gdk_renderer->priv->trapezoids->len > 0 &&
+      gdk_renderer->priv->trapezoid_part != part)
+    flush_trapezoids (gdk_renderer);
+  
+  gdk_renderer->priv->trapezoid_part = part;
+
+  trap.y1 = y1;
+  trap.x11 = x11;
+  trap.x21 = x21;
+  trap.y2 = y2;
+  trap.x12 = x12;
+  trap.x22 = x22;
 
-  info = gdk_pango_context_get_info (context, FALSE);
+  g_array_append_val (gdk_renderer->priv->trapezoids, trap);
+}
+
+/* We can't handle embossing at the level of trapezoids, because when an
+ * underline is split into multiple trapezoids, the normal and shadow
+ * trapezoids will be drawn mixed together. Instead, we have to emboss
+ * and entire rectangle or error underline
+ */
+
+static void
+gdk_pango_renderer_draw_rectangle (PangoRenderer    *renderer,
+                                  PangoRenderPart   part,
+                                  int               x,
+                                  int               y,
+                                  int               width,
+                                  int               height)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
 
-  if (info == NULL || info->colormap == NULL)
+  if (!priv->in_emboss && priv->embossed && part != PANGO_RENDER_PART_BACKGROUND)
     {
-      g_warning ("you must set the colormap on a PangoContext before using it to draw a layout");
-      return NULL;
+      PangoMatrix *save_matrix;
+      PangoColor *save_color;
+
+      emboss_renderer (renderer, part, &save_matrix, &save_color);
+      PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_rectangle (renderer, part,
+                                                                             x, y, width, height);
+      unemboss_renderer (renderer, part, &save_matrix, &save_color);
+    }
+
+  PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_rectangle (renderer, part,
+                                                                         x, y, width, height);
+}
+
+static void
+gdk_pango_renderer_draw_error_underline (PangoRenderer    *renderer,
+                                        int               x,
+                                        int               y,
+                                        int               width,
+                                        int               height)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  if (!priv->in_emboss && priv->embossed)
+    {
+      PangoMatrix *save_matrix;
+      PangoColor *save_color;
+
+      emboss_renderer (renderer, PANGO_RENDER_PART_UNDERLINE, &save_matrix, &save_color);
+      PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_error_underline (renderer,
+                                                                                   x, y, width, height);
+      unemboss_renderer (renderer, PANGO_RENDER_PART_UNDERLINE, &save_matrix, &save_color);
+    }
+
+  PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_error_underline (renderer,
+                                                                               x, y, width, height);
+}
+
+static void
+gdk_pango_renderer_part_changed (PangoRenderer   *renderer,
+                                PangoRenderPart  part)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  if (part == gdk_renderer->priv->trapezoid_part)
+    flush_trapezoids (gdk_renderer);
+}
+
+static void
+gdk_pango_renderer_begin (PangoRenderer *renderer)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  if (!gdk_renderer->priv->drawable || !gdk_renderer->priv->base_gc)
+    {
+      g_warning ("gdk_pango_renderer_set_drawable() and gdk_pango_renderer_set_drawable()"
+                "must be used to set the target drawable and GC before using the renderer\n");
     }
+}
+
+static void
+gdk_pango_renderer_end (PangoRenderer *renderer)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  flush_trapezoids (gdk_renderer);
+}
 
-  result = gdk_gc_new (drawable);
-  gdk_gc_copy (result, base_gc);
+static void
+gdk_pango_renderer_prepare_run (PangoRenderer  *renderer,
+                               PangoLayoutRun *run)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  gboolean embossed = FALSE;
+  GdkBitmap *stipple = NULL;
+  GSList *l;
+  int i;
+  
+  embossed = FALSE;
+  stipple = NULL;
   
-  if (fg_color)
+  for (l = run->item->analysis.extra_attrs; l; l = l->next)
     {
-      GdkColor color;
-      
-      color.red = fg_color->red;
-      color.green = fg_color->green;
-      color.blue = fg_color->blue;
+      PangoAttribute *attr = l->data;
 
-      gdk_rgb_find_color (info->colormap, &color);
-      gdk_gc_set_foreground (result, &color);
+      /* stipple_type and embossed_type aren't necessarily
+       * initialized, but they are 0, which is an
+       * invalid type so won't occur. 
+       */
+      if (attr->klass->type == gdk_pango_attr_stipple_type)
+       {
+         stipple = ((GdkPangoAttrStipple*)attr)->stipple;
+       }
+      else if (attr->klass->type == gdk_pango_attr_embossed_type)
+       {
+         embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
+       }
     }
 
-  if (stipple)
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, stipple);
+
+  if (embossed != gdk_renderer->priv->embossed)
     {
-      gdk_gc_set_fill (result, GDK_STIPPLED);
-      gdk_gc_set_stipple (result, stipple);
+      gdk_renderer->priv->embossed = embossed;
+      pango_renderer_part_changed (renderer, PANGO_RENDER_PART_FOREGROUND);
     }
+
+  PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->prepare_run (renderer, run);
+
+  for (i = 0; i <= MAX_RENDER_PART; i++)
+    {
+      if (gdk_renderer->priv->override_color_set[i])
+       pango_renderer_set_color (renderer, i, &gdk_renderer->priv->override_color[i]);
+    }
+}
+
+static void
+gdk_pango_renderer_set_property (GObject         *object,
+                                guint            prop_id,
+                                const GValue    *value,
+                                GParamSpec      *pspec)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SCREEN:
+      gdk_renderer->priv->screen = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_pango_renderer_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SCREEN:
+      g_value_set_object (value, gdk_renderer->priv->screen);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_pango_renderer_init (GdkPangoRenderer *renderer)
+{
+  renderer->priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer,
+                                               GDK_TYPE_PANGO_RENDERER,
+                                               GdkPangoRendererPrivate);
+}
+
+static void
+gdk_pango_renderer_class_init (GdkPangoRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+  PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
+  
+  renderer_class->draw_glyphs = gdk_pango_renderer_draw_glyphs;
+  renderer_class->draw_trapezoid = gdk_pango_renderer_draw_trapezoid;
+  renderer_class->draw_rectangle = gdk_pango_renderer_draw_rectangle;
+  renderer_class->draw_error_underline = gdk_pango_renderer_draw_error_underline;
+  renderer_class->part_changed = gdk_pango_renderer_part_changed;
+  renderer_class->begin = gdk_pango_renderer_begin;
+  renderer_class->end = gdk_pango_renderer_end;
+  renderer_class->prepare_run = gdk_pango_renderer_prepare_run;
+
+  object_class->finalize = gdk_pango_renderer_finalize;
+  object_class->constructor = gdk_pango_renderer_constructor;
+  object_class->set_property = gdk_pango_renderer_set_property;
+  object_class->get_property = gdk_pango_renderer_get_property;
+  
+  g_object_class_install_property (object_class,
+                                   PROP_SCREEN,
+                                   g_param_spec_object ("screen",
+                                                        P_("Screen"),
+                                                        P_("the GdkScreen for the renderer"),
+                                                        GDK_TYPE_SCREEN,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_type_class_add_private (object_class, sizeof (GdkPangoRendererPrivate));  
+}
+
+/**
+ * gdk_pango_renderer_new:
+ * @screen: a #GdkScreen
+ * 
+ * Creates a new #PangoRenderer for @screen. Normally you can use the
+ * results of gdk_pango_renderer_get_default() rather than creating a new
+ * renderer.
+ * 
+ * Return value: a newly created #PangoRenderer. Free with g_object_unref().
+ *
+ * Since: 2.6
+ **/
+PangoRenderer *
+gdk_pango_renderer_new (GdkScreen *screen)
+{
+  g_return_val_if_fail (screen != NULL, NULL);
   
-  return result;
+  return g_object_new (GDK_TYPE_PANGO_RENDERER,
+                      "screen", screen,
+                      NULL);
 }
 
 static void
-gdk_pango_free_gc (PangoContext *context,
-                  GdkGC        *gc)
+on_renderer_display_closed (GdkDisplay       *display,
+                           GdkPangoRenderer *renderer)
 {
-  g_object_unref (gc);
+  g_signal_handlers_disconnect_by_func (renderer->priv->screen,
+                                       (gpointer)on_renderer_display_closed,
+                                       renderer);
+  g_object_set_data (G_OBJECT (renderer->priv->screen), "gdk-pango-renderer", NULL);
 }
 
 /**
- * gdk_pango_context_set_colormap:
- * @context: a #PangoContext
- * @colormap: a #GdkColormap
+ * gdk_pango_renderer_get_default:
+ * @screen: a #GdkScreen
+ * 
+ * Gets the default #PangoRenderer for a screen. This default renderer
+ * is shared by all users of the display, so properties such as the color
+ * or transformation matrix set for the renderer may be overwritten
+ * by functions such as gdk_draw_layout().
  *
- * Sets the colormap to be used for drawing with @context.
+ * Before using the renderer, you need to call gdk_pango_renderer_set_drawable()
+ * and gdk_pango_renderer_set_drawable() to set the drawable and graphics context
+ * to use for drawing.
+ * 
+ * Return value: the default #PangoRenderer for @screen. The
+ *  renderer is owned by GTK+ and will be kept around until the
+ *  screen is closed.
  *
- * If you obtained your context from gtk_widget_get_pango_context() or
- * gtk_widget_create_pango_context(), the colormap will already be set
- * to the colormap for the widget, so you shouldn't need this
- * function.
+ * Since: 2.6
+ **/
+PangoRenderer *
+gdk_pango_renderer_get_default (GdkScreen *screen)
+{
+  PangoRenderer *renderer;
+
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+  
+  renderer = g_object_get_data (G_OBJECT (screen), "gdk-pango-renderer");
+  if (!renderer)
+    {
+      renderer = gdk_pango_renderer_new (screen);
+      g_object_set_data_full (G_OBJECT (screen), "gdk-pango-renderer", renderer,
+                             (GDestroyNotify)g_object_unref);
+
+      g_signal_connect (gdk_screen_get_display (screen), "closed",
+                       G_CALLBACK (on_renderer_display_closed), renderer);
+    }
+
+  return renderer;
+}
+
+/**
+ * gdk_pango_renderer_set_drawable:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @drawable: the new target drawable, or %NULL
+ * 
+ * Sets the drawable the renderer draws to.
+ *
+ * Since: 2.6
  **/
 void
-gdk_pango_context_set_colormap (PangoContext *context,
-                               GdkColormap  *colormap)
+gdk_pango_renderer_set_drawable (GdkPangoRenderer *gdk_renderer,
+                                GdkDrawable      *drawable)
+{
+  GdkPangoRendererPrivate *priv;
+  
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+  g_return_if_fail (drawable == NULL || GDK_IS_DRAWABLE (drawable));
+
+  priv = gdk_renderer->priv;
+  
+  flush_trapezoids (gdk_renderer);
+
+  if (priv->drawable != drawable)
+    {
+      if (priv->drawable)
+       g_object_unref (priv->drawable);
+      priv->drawable = drawable;
+      if (priv->drawable)
+       g_object_ref (priv->drawable);
+    }
+}
+
+/**
+ * gdk_pango_renderer_set_gc:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @gc: the new GC to use for drawing, or %NULL
+ * 
+ * Sets the GC the renderer draws with. Note that the GC must not be
+ * modified until it is unset by calling the function again with
+ * %NULL for the @gc parameter, since GDK may make internal copies
+ * of the GC which won't be updated to follow changes to the
+ * original GC.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_pango_renderer_set_gc (GdkPangoRenderer *gdk_renderer,
+                          GdkGC            *gc)
 {
-  GdkPangoContextInfo *info;
+  GdkPangoRendererPrivate *priv;
   
-  g_return_if_fail (context != NULL);
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+  g_return_if_fail (gc == NULL || GDK_IS_GC (gc));
 
-  info = gdk_pango_context_get_info (context, TRUE);
-  g_return_if_fail (info != NULL);
+  priv = gdk_renderer->priv;
   
-  if (info->colormap != colormap)
+  flush_trapezoids (gdk_renderer);
+
+  if (priv->base_gc != gc)
     {
-      if (info->colormap)
-       g_object_unref (info->colormap);
+      if (priv->base_gc)
+       g_object_unref (priv->base_gc);
+      priv->base_gc = gc;
+      if (priv->base_gc)
+       g_object_ref (priv->base_gc);
 
-      info->colormap = colormap;
+      if (priv->gc)
+       {
+         g_object_unref (priv->gc);
+         priv->gc = NULL;
+       }
       
-      if (info->colormap)
-       g_object_ref (info->colormap);
+      priv->gc_color_set = FALSE;
+
+      if (priv->gc_stipple)
+       {
+         g_object_unref (priv->gc_stipple);
+         priv->gc_stipple = NULL;
+       }
     }
 }
 
-static void
-draw_underline (GdkDrawable    *drawable,
-               GdkGC          *gc,
-               PangoUnderline  uline,
-               int             baseline_y,
-               int             low_y,
-               int             start_x,
-               int             end_x)
-{
-  switch (uline)
+
+/**
+ * gdk_pango_renderer_set_stipple:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @part: the part to render with the stipple
+ * @stipple: the new stipple value.
+ * 
+ * Sets the stipple for one render part (foreground, background, underline,
+ * etc.) Note that this is overwritten when iterating through the individual
+ * styled runs of a #PangoLayout or #PangoLayoutLine. This function is thus
+ * only useful when you call low level functions like pango_renderer_draw_glyphs()
+ * directly, or in the 'prepare_run' virtual function of a subclass of
+ * #GdkPangoRenderer.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_pango_renderer_set_stipple (GdkPangoRenderer *gdk_renderer,
+                               PangoRenderPart   part,
+                               GdkBitmap        *stipple)
+{
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+
+  if (part > MAX_RENDER_PART)  /* Silently ignore unknown parts */
+    return;
+
+  if (stipple != gdk_renderer->priv->stipple[part])
     {
-    case PANGO_UNDERLINE_NONE:
-      break;
-    case PANGO_UNDERLINE_DOUBLE:
-      gdk_draw_line (drawable, gc,
-                    start_x, baseline_y + 3,
-                    end_x,   baseline_y + 3);
-      /* Fall through */
-    case PANGO_UNDERLINE_SINGLE:
-      gdk_draw_line (drawable, gc,
-                    start_x, baseline_y + 1,
-                    end_x,   baseline_y + 1);
-      break;
-    case PANGO_UNDERLINE_ERROR:
-      {
-        int point_x, point_y;
-        int counter = 0;
-
-        for (point_x = start_x;
-             point_x <= end_x;
-             point_x += 2)
-         {
-           point_y = counter ? baseline_y + 1 : baseline_y + 2;
-           
-           gdk_draw_line (drawable, gc,
-                          point_x, point_y,
-                          MIN (point_x + 1, end_x), point_y);
-           
-           counter = (counter + 1) % 2;
-         }
-      }
-      break;
-    case PANGO_UNDERLINE_LOW:
-      gdk_draw_line (drawable, gc,
-                    start_x, low_y + 1,
-                    end_x,   low_y + 1);
-      break;
+      if (gdk_renderer->priv->stipple[part])
+       g_object_unref (gdk_renderer->priv->stipple[part]);
+
+      gdk_renderer->priv->stipple[part] = stipple;
+      
+      if (gdk_renderer->priv->stipple[part])
+       g_object_ref (gdk_renderer->priv->stipple[part]);
+
+      pango_renderer_part_changed (PANGO_RENDERER (gdk_renderer), part);
     }
 }
 
+/**
+ * gdk_pango_renderer_set_override_color:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @part: the part to render to set the color of
+ * @color: the color to use, or %NULL to unset a previously
+ *         set override color.
+ * 
+ * Sets the color for a particular render part (foreground,
+ * background, underline, etc.), overriding any attributes on the layouts
+ * renderered with this renderer.
+ * 
+ * Since: 2.6
+ **/
+void
+gdk_pango_renderer_set_override_color (GdkPangoRenderer *gdk_renderer,
+                                      PangoRenderPart   part,
+                                      const GdkColor   *color)
+{
+  GdkPangoRendererPrivate *priv;
+  
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+
+  priv = gdk_renderer->priv;
+  
+  if (part > MAX_RENDER_PART)  /* Silently ignore unknown parts */
+    return;
+
+  if (color)
+    {
+      priv->override_color[part].red = color->red;
+      priv->override_color[part].green = color->green;
+      priv->override_color[part].blue = color->blue;
+      priv->override_color_set[part] = TRUE;
+    }
+  else
+    priv->override_color_set[part] = FALSE;
+}
+
+/**
+ * gdk_pango_context_set_colormap:
+ * @context: a #PangoContext
+ * @colormap: a #GdkColormap
+ *
+ * This function used to set the colormap to be used for drawing with
+ * @context. The colormap is now always derived from the graphics
+ * context used for drawing, so calling this function is no longer
+ * necessary.
+ **/
+void
+gdk_pango_context_set_colormap (PangoContext *context,
+                               GdkColormap  *colormap)
+{
+  g_return_if_fail (PANGO_IS_CONTEXT (context));
+  g_return_if_fail (colormap == NULL || GDK_IS_COLORMAP (colormap));
+}
+
+/* Gets a renderer to draw with, setting the properties of the
+ * renderer and activating it. Note that since we activate the
+ * renderer here, the implicit setting of the matrix that
+ * pango_renderer_draw_layout_[line] normally do when they
+ * activate the renderer is suppressed. */
+static PangoRenderer *
+get_renderer (GdkDrawable     *drawable,
+             GdkGC           *gc,
+             const GdkColor  *foreground,
+             const GdkColor  *background)
+{
+  GdkScreen *screen = gdk_drawable_get_screen (drawable);
+  PangoRenderer *renderer = gdk_pango_renderer_get_default (screen);
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  gdk_pango_renderer_set_drawable (gdk_renderer, drawable);
+  gdk_pango_renderer_set_gc (gdk_renderer, gc);  
+
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_FOREGROUND,
+                                        foreground);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_UNDERLINE,
+                                        foreground);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_STRIKETHROUGH,
+                                        foreground);
+
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_BACKGROUND,
+                                        background);
+
+  pango_renderer_activate (renderer);
+
+  return renderer;
+}
+
+/* Cleans up the renderer obtained with get_renderer() */
+static void
+release_renderer (PangoRenderer *renderer)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  
+  pango_renderer_deactivate (renderer);
+  
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_FOREGROUND,
+                                        NULL);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_UNDERLINE,
+                                        NULL);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_STRIKETHROUGH,
+                                        NULL);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_BACKGROUND,
+                                        NULL);
+  
+  gdk_pango_renderer_set_drawable (gdk_renderer, NULL);
+  gdk_pango_renderer_set_gc (gdk_renderer, NULL);
+}
+
 /**
  * gdk_draw_layout_line_with_colors:
  * @drawable:  the drawable on which to draw the line
@@ -228,6 +904,11 @@ draw_underline (GdkDrawable    *drawable,
  * Render a #PangoLayoutLine onto a #GdkDrawable, overriding the
  * layout's normal colors with @foreground and/or @background.
  * @foreground and @background need not be allocated.
+ *
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the left edge of the baseline
+ * (left is in before-tranform user coordinates) in after-transform
+ * device coordinates.
  */
 void 
 gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
@@ -238,215 +919,88 @@ gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
                                   const GdkColor   *foreground,
                                   const GdkColor   *background)
 {
-  GSList *tmp_list = line->runs;
-  PangoRectangle overall_rect;
-  PangoRectangle logical_rect;
-  PangoRectangle ink_rect;
-  PangoContext *context;
-  gint x_off = 0;
-  gint rise = 0;
-  gboolean embossed;
-  GdkBitmap *stipple;
-  PangoUnderline last_uline = PANGO_UNDERLINE_NONE;
-  gint uline_start_x = 0;
-  gint uline_end_x = 0;
-  gint uline_end_x_extended = 0;
-  gint last_risen_y = 0;
-  gint low_y = G_MININT;
-  GdkGC *last_fg_gc = NULL;
-  gboolean last_fg_set = FALSE;
-  PangoColor last_fg_color;
-
+  PangoRenderer *renderer;
+  const PangoMatrix *matrix;
+  
   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
   g_return_if_fail (GDK_IS_GC (gc));
   g_return_if_fail (line != NULL);
 
-  context = pango_layout_get_context (line->layout);
-  
-  pango_layout_line_get_extents (line,NULL, &overall_rect);
-  
-  while (tmp_list)
+  renderer = get_renderer (drawable, gc, foreground, background);
+
+  /* When we have a matrix, we do positioning by adjusting the matrix, and
+   * clamp just pass x=0, y=0 to the lower levels. We don't want to introduce
+   * a matrix when the caller didn't provide one, however, since that adds
+   * lots of floating point arithmetic for each glyph.
+   */
+  matrix = pango_context_get_matrix (pango_layout_get_context (line->layout));
+  if (matrix)
     {
-      PangoUnderline this_uline = PANGO_UNDERLINE_NONE;
-      PangoLayoutRun *run = tmp_list->data;
-      PangoColor fg_color, bg_color;
-      gboolean strike, fg_set, bg_set, shape_set;
-      GdkGC *fg_gc;
-      gint risen_y;
-      
-      tmp_list = tmp_list->next;
+      PangoMatrix tmp_matrix;
       
-      gdk_pango_get_item_properties (run->item, &this_uline,
-                                    &strike,
-                                     &rise,
-                                     &fg_color, &fg_set,
-                                     &bg_color, &bg_set,
-                                     &embossed,
-                                     &stipple,
-                                     &shape_set,
-                                     &ink_rect,
-                                    &logical_rect);
-
-      /* we subtract the rise because X coordinates are upside down */
-      risen_y = y - rise / PANGO_SCALE;
-      
-      if (!shape_set)
-       {
-         if (this_uline == PANGO_UNDERLINE_NONE)
-           pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
-                                       NULL, &logical_rect);
-         else
-           pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
-                                       &ink_rect, &logical_rect);
-       }
+      tmp_matrix = *matrix;
+      tmp_matrix.x0 = x;
+      tmp_matrix.y0 = y;
+      pango_renderer_set_matrix (renderer, &tmp_matrix);
 
-      if (bg_set || background)
-       {
-         GdkGC *bg_gc;
-          PangoColor tmp = bg_color;
-          
-          if (background)
-            {
-              tmp.red = background->red;
-              tmp.blue = background->blue;
-              tmp.green = background->green;
-            }
-          
-          bg_gc = gdk_pango_get_gc (drawable, context, &tmp, stipple, gc);
-          
-         gdk_draw_rectangle (drawable, bg_gc, TRUE,
-                             x + (x_off + logical_rect.x) / PANGO_SCALE,
-                             risen_y + overall_rect.y / PANGO_SCALE,
-                             logical_rect.width / PANGO_SCALE,
-                             overall_rect.height / PANGO_SCALE);
-
-          if (stipple)
-            gdk_gc_set_fill (bg_gc, GDK_SOLID);
-          
-         gdk_pango_free_gc (context, bg_gc);
-       }
+      x = 0;
+      y = 0;
+    }
+  else
+    pango_renderer_set_matrix (renderer, NULL);
 
-      if (fg_set || stipple || foreground)
-        {
-          PangoColor tmp = fg_color;
-          
-          if (foreground)
-            {
-              tmp.red = foreground->red;
-              tmp.blue = foreground->blue;
-              tmp.green = foreground->green;
-            }
-          
-          fg_gc = gdk_pango_get_gc (drawable, context, (fg_set || foreground) ? &tmp : NULL,
-                                    stipple, gc);
-        }
-      else
-       fg_gc = gc;
+  pango_renderer_draw_layout_line (renderer, line, x * PANGO_SCALE, y * PANGO_SCALE);
 
-      if (!shape_set)
-        {
-          gint gx, gy;
+  release_renderer (renderer);
+}
 
-          gx = x + x_off / PANGO_SCALE;
-          gy = risen_y;
-          
-          if (embossed)
-            {
-              PangoColor color = { 65535, 65535, 65535 };
-              GdkGC *white_gc = gdk_pango_get_gc (drawable, context, &color, stipple, fg_gc);
-              gdk_draw_glyphs (drawable, white_gc, run->item->analysis.font,
-                               gx + 1,
-                               gy + 1,
-                               run->glyphs);
-              gdk_pango_free_gc (context, white_gc);
-            }
-          
-          gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
-                           gx, gy,
-                           run->glyphs);
-        }
+/* Gets the bounds of a layout in device coordinates. Note cut-and-paste
+ * between here and gtklabel.c */
+static void
+get_rotated_layout_bounds (PangoLayout  *layout,
+                          GdkRectangle *rect)
+{
+  PangoContext *context = pango_layout_get_context (layout);
+  const PangoMatrix *matrix = pango_context_get_matrix (context);
+  gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */
+  PangoRectangle logical_rect;
+  gint i, j;
 
-      if (this_uline != last_uline ||
-         risen_y != last_risen_y ||
-         fg_set != last_fg_set ||
-         (fg_set && (last_fg_color.red != fg_color.red ||
-                     last_fg_color.green != fg_color.green ||
-                     last_fg_color.blue != fg_color.blue)))
+  pango_layout_get_extents (layout, NULL, &logical_rect);
+  
+  for (i = 0; i < 2; i++)
+    {
+      gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width;
+      for (j = 0; j < 2; j++)
        {
-         /* If only color changes, the underlines extend to the edge
-          * of the logical rectangle so they join up; otherwise they
-          * go 1 pixel beyond the ink rectangle. This doesn't work
-          * for low underlines (they will be at a different y anyways),
-          * so they follow the normal path.
-          */
-         gboolean extend_uline = (this_uline == last_uline &&
-                                  this_uline != PANGO_UNDERLINE_LOW &&
-                                  risen_y == last_risen_y);
+         gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height;
+         
+         gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
+         gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
          
-         /* Starting a new underline run
-          */
-         if (last_uline != PANGO_UNDERLINE_NONE)
+         if (i == 0 && j == 0)
            {
-             draw_underline (drawable, last_fg_gc, last_uline,
-                             last_risen_y, low_y,
-                             uline_start_x,
-                             extend_uline ? uline_end_x_extended : uline_end_x);
+             x_min = x_max = xt;
+             y_min = y_max = yt;
            }
-
-         if (this_uline != PANGO_UNDERLINE_NONE)
+         else
            {
-             if (extend_uline)
-               uline_start_x = x + x_off / PANGO_SCALE;
-             else
-               uline_start_x = x + (x_off + ink_rect.x) / PANGO_SCALE - 1;
-
-             low_y = G_MININT;
+             if (xt < x_min)
+               x_min = xt;
+             if (yt < y_min)
+               y_min = yt;
+             if (xt > x_max)
+               x_max = xt;
+             if (yt > y_max)
+               y_max = yt;
            }
        }
-
-      /* Update current underline segment information
-       */
-      if (this_uline != PANGO_UNDERLINE_NONE)
-       {
-         uline_end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE;
-         uline_end_x_extended = x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE - 1;
-       }
-
-      if (this_uline == PANGO_UNDERLINE_LOW)
-       low_y = MAX (low_y, risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE);
-
-      if (strike)
-       {
-         int centerline = logical_rect.y + logical_rect.height / 2;
-         
-         gdk_draw_line (drawable, fg_gc,
-                        x + (x_off + logical_rect.x) / PANGO_SCALE - 1,
-                        risen_y + centerline / PANGO_SCALE,
-                        x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE + 1,
-                        risen_y + centerline / PANGO_SCALE);
-       }
-
-      if (last_fg_gc != gc && last_fg_gc)
-       gdk_pango_free_gc (context, last_fg_gc);
-
-      last_risen_y = risen_y;
-      last_uline = this_uline;
-      last_fg_gc = fg_gc;
-      last_fg_set = fg_set;
-      if (fg_set)
-       last_fg_color = fg_color;
-      
-      x_off += logical_rect.width;
     }
-
-  /* Finish off any remaining underlines
-   */
-  if (last_uline != PANGO_UNDERLINE_NONE)
-    draw_underline (drawable, last_fg_gc, last_uline, last_risen_y, low_y,
-                   uline_start_x, uline_end_x);
-
-  if (last_fg_gc != gc && last_fg_gc)
-    gdk_pango_free_gc (context, last_fg_gc);
+  
+  rect->x = floor (x_min);
+  rect->width = ceil (x_max) - rect->x;
+  rect->y = floor (y_min);
+  rect->height = floor (y_max) - rect->y;
 }
 
 /**
@@ -463,6 +1017,10 @@ gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
  * layout's normal colors with @foreground and/or @background.
  * @foreground and @background need not be allocated.
  *
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the top left corner of the
+ * bounding box (in device space) of the transformed layout.
+ *
  * If you're using GTK+, the ususal way to obtain a #PangoLayout
  * is gtk_widget_create_pango_layout().
  */
@@ -475,35 +1033,42 @@ gdk_draw_layout_with_colors (GdkDrawable     *drawable,
                              const GdkColor  *foreground,
                              const GdkColor  *background)
 {
-  PangoLayoutIter *iter;
+  PangoRenderer *renderer;
+  const PangoMatrix *matrix;
   
   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
   g_return_if_fail (GDK_IS_GC (gc));
   g_return_if_fail (PANGO_IS_LAYOUT (layout));
 
-  iter = pango_layout_get_iter (layout);
-  
-  do
+  renderer = get_renderer (drawable, gc, foreground, background);
+
+  /* When we have a matrix, we do positioning by adjusting the matrix, and
+   * clamp just pass x=0, y=0 to the lower levels. We don't want to introduce
+   * a matrix when the caller didn't provide one, however, since that adds
+   * lots of floating point arithmetic for each glyph.
+   */
+  matrix = pango_context_get_matrix (pango_layout_get_context (layout));
+  if (matrix)
     {
-      PangoRectangle logical_rect;
-      PangoLayoutLine *line;
-      int baseline;
-      
-      line = pango_layout_iter_get_line (iter);
+      PangoMatrix tmp_matrix;
+      GdkRectangle rect;
+
+      get_rotated_layout_bounds (layout, &rect);
       
-      pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
-      baseline = pango_layout_iter_get_baseline (iter);
+      tmp_matrix = *matrix;
+      tmp_matrix.x0 += x - rect.x;
+      tmp_matrix.y0 += y - rect.y;
+      pango_renderer_set_matrix (renderer, &tmp_matrix);
       
-      gdk_draw_layout_line_with_colors (drawable, gc,
-                                        x + logical_rect.x / PANGO_SCALE,
-                                        y + baseline / PANGO_SCALE,
-                                        line,
-                                        foreground,
-                                        background);
+      x = 0;
+      y = 0;
     }
-  while (pango_layout_iter_next_line (iter));
-
-  pango_layout_iter_free (iter);
+  else
+    pango_renderer_set_matrix (renderer, NULL);
+  
+  pango_renderer_draw_layout (renderer, layout, x * PANGO_SCALE, y * PANGO_SCALE);
+  
+  release_renderer (renderer);
 }
 
 /**
@@ -515,6 +1080,11 @@ gdk_draw_layout_with_colors (GdkDrawable     *drawable,
  * @line:      a #PangoLayoutLine
  *
  * Render a #PangoLayoutLine onto an GDK drawable
+ *
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the left edge of the baseline
+ * (left is in before-tranform user coordinates) in after-transform
+ * device coordinates.
  */
 void 
 gdk_draw_layout_line (GdkDrawable      *drawable,
@@ -540,7 +1110,11 @@ gdk_draw_layout_line (GdkDrawable      *drawable,
  *
  * Render a #PangoLayout onto a GDK drawable
  *
- * If you're using GTK+, the ususal way to obtain a #PangoLayout
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the top left corner of the
+ * bounding box (in device space) of the transformed layout.
+ *
+ * If you're using GTK+, the usual way to obtain a #PangoLayout
  * is gtk_widget_create_pango_layout().
  */
 void 
@@ -557,110 +1131,6 @@ gdk_draw_layout (GdkDrawable     *drawable,
   gdk_draw_layout_with_colors (drawable, gc, x, y, layout, NULL, NULL);
 }
 
-static void
-gdk_pango_get_item_properties (PangoItem      *item,
-                              PangoUnderline *uline,
-                              gboolean       *strikethrough,
-                               gint           *rise,
-                              PangoColor     *fg_color,
-                              gboolean       *fg_set,
-                              PangoColor     *bg_color,
-                              gboolean       *bg_set,
-                               gboolean       *embossed,
-                               GdkBitmap     **stipple,
-                              gboolean       *shape_set,
-                              PangoRectangle *ink_rect,
-                              PangoRectangle *logical_rect)
-{
-  GSList *tmp_list = item->analysis.extra_attrs;
-
-  if (strikethrough)
-      *strikethrough = FALSE;
-  
-  if (fg_set)
-    *fg_set = FALSE;
-  
-  if (bg_set)
-    *bg_set = FALSE;
-
-  if (shape_set)
-    *shape_set = FALSE;
-
-  if (rise)
-    *rise = 0;
-
-  if (embossed)
-    *embossed = FALSE;
-
-  if (stipple)
-    *stipple = NULL;
-  
-  while (tmp_list)
-    {
-      PangoAttribute *attr = tmp_list->data;
-
-      switch (attr->klass->type)
-       {
-       case PANGO_ATTR_UNDERLINE:
-         if (uline)
-           *uline = ((PangoAttrInt *)attr)->value;
-         break;
-
-       case PANGO_ATTR_STRIKETHROUGH:
-           if (strikethrough)
-               *strikethrough = ((PangoAttrInt *)attr)->value;
-           break;
-           
-       case PANGO_ATTR_FOREGROUND:
-         if (fg_color)
-           *fg_color = ((PangoAttrColor *)attr)->color;
-         if (fg_set)
-           *fg_set = TRUE;
-         
-         break;
-         
-       case PANGO_ATTR_BACKGROUND:
-         if (bg_color)
-           *bg_color = ((PangoAttrColor *)attr)->color;
-         if (bg_set)
-           *bg_set = TRUE;
-         
-         break;
-
-       case PANGO_ATTR_SHAPE:
-         if (shape_set)
-           *shape_set = TRUE;
-         if (logical_rect)
-           *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
-         if (ink_rect)
-           *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
-         break;
-
-        case PANGO_ATTR_RISE:
-          if (rise)
-            *rise = ((PangoAttrInt *)attr)->value;
-          break;
-          
-       default:
-          /* stipple_type and embossed_type aren't necessarily
-           * initialized, but they are 0, which is an
-           * invalid type so won't occur. 
-           */
-          if (stipple && attr->klass->type == gdk_pango_attr_stipple_type)
-            {
-              *stipple = ((GdkPangoAttrStipple*)attr)->stipple;
-            }
-          else if (embossed && attr->klass->type == gdk_pango_attr_embossed_type)
-            {
-              *embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
-            }
-         break;
-       }
-      tmp_list = tmp_list->next;
-    }
-}
-
-
 static PangoAttribute *
 gdk_pango_attr_stipple_copy (const PangoAttribute *attr)
 {