OSDN Git Service

2004-11-30 Thomas Fitzsimmons <fitzsim@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / jni / gtk-peer / gnu_java_awt_peer_gtk_GdkGraphics2D.c
index 733461d..90b6aa1 100644 (file)
@@ -1,5 +1,5 @@
 /* gnu_java_awt_peer_gtk_GdkGraphics2d.c
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
 
    This file is part of GNU Classpath.
    
@@ -64,7 +64,8 @@ struct state_table *native_graphics2d_state_table;
 #define NSA_DEL_G2D_PTR(env, obj) \
   remove_state_slot (env, obj, native_graphics2d_state_table)
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initStaticState 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initStaticState 
   (JNIEnv *env, jclass clazz)
 {
    gdk_threads_enter();
@@ -159,18 +160,7 @@ grab_current_drawable (GtkWidget *widget, GdkDrawable **draw, GdkWindow **win)
   g_assert (draw != NULL);
   g_assert (win != NULL);
 
-  if (GTK_IS_WINDOW (widget))
-    {
-      *win = find_gtk_layout (widget)->bin_window;
-    }
-  else if (GTK_IS_LAYOUT (widget))
-    {
-      *win = GTK_LAYOUT (widget)->bin_window;
-    }
-  else
-    {
-      *win = widget->window;
-    }
+  *win = widget->window;
 
   *draw = *win;
   gdk_window_get_internal_paint_info (*win, draw, 0, 0); 
@@ -326,7 +316,18 @@ check_for_debug (struct graphics2d *gr)
   gr->debug = (gboolean)(getenv("DEBUGJ2D") != NULL);
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_copyState
+static void
+realize_cb (GtkWidget *widget, jobject peer)
+{
+  gdk_threads_leave ();
+
+  (*gdk_env)->CallVoidMethod (gdk_env, peer, initComponentGraphics2DID);
+
+  gdk_threads_enter ();
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_copyState
   (JNIEnv *env, jobject obj, jobject old)
 {
   struct graphics2d *g = NULL, *g_old = NULL;
@@ -361,7 +362,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_copyState
 }
 
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__II
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__II
   (JNIEnv *env, jobject obj, jint width, jint height)
 {
   struct graphics2d *gr;
@@ -394,13 +396,13 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__II
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_gdkDrawDrawable
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_gdkDrawDrawable
   (JNIEnv *env, jobject self, jobject other, jint x, jint y)
 {
   struct graphics2d *src = NULL, *dst = NULL;
   gint s_height, s_width, d_height, d_width, height, width;
   cairo_matrix_t *matrix;
-  GdkGC *gc;
   cairo_operator_t tmp_op;
 
   gdk_threads_enter();
@@ -414,7 +416,7 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_gdkDrawDrawable
 
   begin_drawing_operation(dst); 
 
-  gdk_flush();
+  /* gdk_flush(); */
 
   gdk_drawable_get_size (src->drawable, &s_width, &s_height);
   gdk_drawable_get_size (dst->drawable, &d_width, &d_height);
@@ -443,41 +445,13 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_gdkDrawDrawable
   gdk_threads_leave();
 }
 
-static jintArray
-current_colors_of_widget (GtkWidget *widget, JNIEnv *env)
-{
-  GdkColor color;
-  jintArray array;
-  jint *rgb;
-
-  g_assert (widget != NULL);
-  g_assert (env != NULL);
-
-  color = widget->style->fg[GTK_STATE_NORMAL];
-  array = (*env)->NewIntArray (env, 6);
-
-  rgb = (*env)->GetIntArrayElements (env, array, NULL);
-  rgb[0] = color.red >> 8;
-  rgb[1] = color.green >> 8;
-  rgb[2] = color.blue >> 8;
-
-  color = widget->style->bg[GTK_STATE_NORMAL];
-  rgb[3] = color.red >> 8;
-  rgb[4] = color.green >> 8;
-  rgb[5] = color.blue >> 8;
-
-  (*env)->ReleaseIntArrayElements (env, array, rgb, 0);
-  
-  return array;
-}
-
-JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__Lgnu_java_awt_peer_gtk_GtkComponentPeer_2
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__Lgnu_java_awt_peer_gtk_GtkComponentPeer_2
   (JNIEnv *env, jobject obj, jobject peer)
 {
   struct graphics2d *gr = NULL;
   GtkWidget *widget = NULL;
   void *ptr = NULL;
-  jintArray color;
 
   gdk_threads_enter();
   if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
@@ -504,14 +478,28 @@ JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_initState__
   else
     init_graphics2d_as_pixbuf (gr);
 
-  color = current_colors_of_widget (widget, env);
-
   NSA_SET_G2D_PTR (env, obj, gr);
   gdk_threads_leave();
-  return color;
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_dispose
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_connectSignals
+  (JNIEnv *env, jobject obj, jobject peer)
+{
+  void *ptr;
+
+  ptr = NSA_GET_PTR (env, peer);
+
+  gdk_threads_enter ();
+
+  g_signal_connect_after (G_OBJECT (ptr), "realize",
+                          G_CALLBACK (realize_cb), obj);
+
+  gdk_threads_leave ();
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_dispose
   (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -550,7 +538,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_dispose
 }
 
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setGradient 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setGradient 
   (JNIEnv *env, jobject obj, 
    jdouble x1, jdouble y1, 
    jdouble x2, jdouble y2,
@@ -637,20 +626,21 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setGradient
      negate offsets. oh well.
      
    */
-
-  double a = (x2 - x1 == 0.) ? 0. : ((cyclic ? 3.0 : 2.0) / (x2 - x1));
-  double c = (y2 - y1 == 0.) ? 0. : (1. / (y2 - y1));
-  double dx = (x1 == 0.) ? 0. : 1. / x1;
-  double dy = (y1 == 0.) ? 0. : 1. / y1;
-
-  cairo_matrix_set_affine (mat,
-                          a, 0.,
-                          c, 0.,
-                          dx, dy);
-
-  cairo_surface_set_matrix (surf, mat);
-  cairo_matrix_destroy (mat);
-  cairo_surface_set_filter (surf, CAIRO_FILTER_BILINEAR);
+  {
+    double a = (x2 - x1 == 0.) ? 0. : ((cyclic ? 3.0 : 2.0) / (x2 - x1));
+    double c = (y2 - y1 == 0.) ? 0. : (1. / (y2 - y1));
+    double dx = (x1 == 0.) ? 0. : 1. / x1;
+    double dy = (y1 == 0.) ? 0. : 1. / y1;
+    
+    cairo_matrix_set_affine (mat,
+                            a, 0.,
+                            c, 0.,
+                            dx, dy);
+    
+    cairo_surface_set_matrix (surf, mat);
+    cairo_matrix_destroy (mat);
+    cairo_surface_set_filter (surf, CAIRO_FILTER_BILINEAR);
+  }
 
   /* FIXME: repeating gradients (not to mention hold gradients) don't seem to work. */
   /*   cairo_surface_set_repeat (surf, cyclic ? 1 : 0); */
@@ -673,7 +663,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setGradient
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setTexturePixels 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setTexturePixels 
   (JNIEnv *env, jobject obj, jintArray jarr, jint w, jint h, jint stride)
 {
   struct graphics2d *gr = NULL;
@@ -719,7 +710,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_setTexturePixels
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_drawPixels 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_drawPixels 
   (JNIEnv *env, jobject obj, jintArray java_pixels, 
    jint w, jint h, jint stride, jdoubleArray java_matrix)
 {
@@ -756,6 +748,7 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_drawPixels
                            native_matrix[4], native_matrix[5]);
    cairo_surface_set_matrix (surf, mat);
    cairo_surface_set_filter (surf, cairo_surface_get_filter(gr->surface));
+
    cairo_show_surface (gr->cr, surf, w, h);
    cairo_matrix_destroy (mat);
    cairo_surface_destroy (surf);
@@ -769,7 +762,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_drawPixels
   gdk_threads_leave();
 }
 
-JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getImagePixels 
+JNIEXPORT jintArray JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getImagePixels 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -780,10 +774,10 @@ JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getImagePix
   gint bits_per_sample = 8;
   gboolean has_alpha = TRUE;
   gint total_channels = 4;
-  jint i, px;
+  jint i;
 
   gdk_threads_enter();
-  if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
+  if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return NULL; }
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
   g_assert (gr != NULL);
@@ -808,24 +802,14 @@ JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getImagePix
                                 width, height);
                                                                                                      
   native_pixels= gdk_pixbuf_get_pixels (buf);
-  
-     
-  /* NOTE: The pixels we got in the pixbuf are stored 
-     in reversed order. i.e 0xBBGGRRAA. 
-     We need to convert them to  0xAARRGGBB. */
-   
-  for (i=0; i<width * height; i++) 
-    {       
-             
-        /* convert pixels from 0xBBGGRRAA to 0xAARRGGBB */
-        
-        px = native_pixels[i];
-        px = ((px >> 24) & 0xff) | ((px << 8) & 0xffffff00); 
-        px = ((px >>  8) & 0x00ff00ff) | ((px <<  8) & 0xff00ff00); 
-        px = ((px >> 16) & 0x0000ffff) | ((px << 16) & 0xffff0000); 
-        native_pixels[i] = px;
-      
+
+#ifndef WORDS_BIGENDIAN
+  /* convert pixels from 0xBBGGRRAA to 0xAARRGGBB */
+  for (i=0; i<width * height; i++)
+    {
+      native_pixels[i] = SWAPU32 ((unsigned)native_pixels[i]);
     }
+#endif
 
    java_pixels = (*env) -> NewIntArray (env, width * height);   
    
@@ -839,7 +823,8 @@ JNIEXPORT jintArray JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getImagePix
 
 /* passthrough methods to cairo */
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSave 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSave 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -854,7 +839,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSave
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRestore 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRestore 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -870,7 +856,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRestore
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMatrix 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMatrix 
    (JNIEnv *env, jobject obj, jdoubleArray java_matrix)
 {
   struct graphics2d *gr = NULL;
@@ -880,7 +867,9 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMatrix
   if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
-  g_assert (gr != NULL);
+  /* cairoSetMatrix was called before this graphics object's component
+     was realized. */
+  if (gr == NULL) { gdk_threads_leave (); return; }
 
   native_matrix = (*env)->GetDoubleArrayElements (env, java_matrix, NULL);  
   g_assert (native_matrix != NULL);
@@ -906,92 +895,385 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMatrix
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetFont 
-   (JNIEnv *env, jobject obj, jobject font)
+static void
+install_font_peer(cairo_t *cr,
+                 struct peerfont *pfont,
+                 int debug)
 {
-  struct graphics2d *gr = NULL;
-  struct peerfont *pfont = NULL;
-  cairo_font_t *ft = NULL;
+  cairo_font_t *ft;
   FT_Face face = NULL;
 
+  g_assert(cr != NULL);
+  g_assert(pfont != NULL);
+  
+  if (pfont->graphics_resource == NULL)
+    {
+      face = pango_ft2_font_get_face (pfont->font);
+      g_assert (face != NULL);
+      
+      ft = cairo_ft_font_create_for_ft_face (face);
+      g_assert (ft != NULL);
+      
+      if (debug) printf ("install_font_peer made new cairo font for '%s' at %f\n", 
+                        face->family_name,
+                        (pango_font_description_get_size (pfont->desc) / 
+                         (double)PANGO_SCALE) * (96.0 / 72.0));
+    
+      cairo_set_font (cr, ft); 
+      cairo_font_destroy (ft);
+      cairo_scale_font (cr, 
+                       (pango_font_description_get_size (pfont->desc) / 
+                        (double)PANGO_SCALE) * (96.0 / 72.0));
+      ft = cairo_current_font (cr);
+      pfont->graphics_resource = ft;
+    }
+  else
+    {
+      if (debug) printf ("install_font_peer reused existing font resource\n");
+      ft = (cairo_font_t *) pfont->graphics_resource;
+      cairo_set_font (cr, ft);       
+    }
+}
+
+static cairo_t *metrics_cairo = NULL;
+static cairo_surface_t *metrics_surface = NULL;
+
+static void
+ensure_metrics_cairo()
+{
+  if (metrics_cairo == NULL)
+    {
+      metrics_cairo = cairo_create ();
+      metrics_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1);
+      cairo_set_target_surface (metrics_cairo, metrics_surface);
+    }
+}
+
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_releasePeerGraphicsResource
+   (JNIEnv *env, jclass clazz, jobject java_font)
+{
+  struct peerfont *pfont = NULL;
+
+  g_assert(java_font != NULL);
+
   gdk_threads_enter();
-  if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
+  pfont = (struct peerfont *) NSA_GET_FONT_PTR (env, java_font);
+  g_assert (pfont != NULL);
+  if (pfont->graphics_resource != NULL)
+    {
+      cairo_font_destroy ((cairo_font_t *) pfont->graphics_resource);
+      pfont->graphics_resource = NULL;
+    }
+  gdk_threads_leave();
+}
 
-  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
-  g_assert (gr != NULL);
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getPeerTextMetrics
+   (JNIEnv *env, jclass clazz, jobject java_font, jstring str, jdoubleArray java_metrics)
+{
+  struct peerfont *pfont = NULL;
+  const char *cstr = NULL;
+  jdouble *native_metrics = NULL;
+  cairo_text_extents_t extents;
+
+  g_assert(java_font != NULL);
+  gdk_threads_enter();
 
-  pfont = (struct peerfont *)NSA_GET_FONT_PTR (env, font);
+  pfont = (struct peerfont *)NSA_GET_FONT_PTR (env, java_font);
   g_assert (pfont != NULL);
 
-  face = pango_ft2_font_get_face (pfont->font);
-  g_assert (face != NULL);
+  ensure_metrics_cairo();
+  install_font_peer (metrics_cairo, pfont, 0);
+
+  cstr = (*env)->GetStringUTFChars (env, str, NULL);
+  g_assert(cstr != NULL);
+  cairo_text_extents (metrics_cairo, cstr, &extents);
+
+  native_metrics = (*env)->GetDoubleArrayElements (env, java_metrics, NULL);
+  g_assert (native_metrics != NULL);
+
+  native_metrics[TEXT_METRICS_X_BEARING] = extents.x_bearing;
+  native_metrics[TEXT_METRICS_Y_BEARING] = extents.y_bearing;
+  native_metrics[TEXT_METRICS_WIDTH] = extents.width;
+  native_metrics[TEXT_METRICS_HEIGHT] = extents.height;
+  native_metrics[TEXT_METRICS_X_ADVANCE] = extents.x_advance;
+  native_metrics[TEXT_METRICS_Y_ADVANCE] = extents.y_advance;
+        
+  (*env)->ReleaseStringUTFChars (env, str, cstr);  
+  (*env)->ReleaseDoubleArrayElements (env, java_metrics, native_metrics, 0);
+  gdk_threads_leave();
+}
 
-  ft = cairo_ft_font_create_for_ft_face (face);
-  g_assert (ft != NULL);
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_getPeerFontMetrics
+   (JNIEnv *env, jclass clazz, jobject java_font, jdoubleArray java_metrics)
+{
+  struct peerfont *pfont = NULL;
+  jdouble *native_metrics = NULL;
+  cairo_font_extents_t extents;
 
-  if (gr->debug) printf ("cairo_set_font '%s'\n", face->family_name);
-  
-  cairo_set_font (gr->cr, ft);
+  g_assert(java_font != NULL);
+
+  gdk_threads_enter();
+
+  pfont = (struct peerfont *)NSA_GET_FONT_PTR (env, java_font);
+  g_assert (pfont != NULL);
+
+  ensure_metrics_cairo();
+  install_font_peer (metrics_cairo, pfont, 0);
+
+  cairo_current_font_extents (metrics_cairo, &extents); 
 
-  cairo_scale_font (gr->cr, 
-                   pango_font_description_get_size (pfont->desc) / 
-                   (double)PANGO_SCALE);
+  native_metrics = (*env)->GetDoubleArrayElements (env, java_metrics, NULL);
+  g_assert (native_metrics != NULL);
 
-  cairo_font_destroy (ft);
+  native_metrics[FONT_METRICS_ASCENT] = extents.ascent;
+  native_metrics[FONT_METRICS_MAX_ASCENT] = extents.ascent;
+  native_metrics[FONT_METRICS_DESCENT] = extents.descent;
+  if (native_metrics[FONT_METRICS_DESCENT] < 0)
+    native_metrics[FONT_METRICS_DESCENT] = - native_metrics[FONT_METRICS_DESCENT];
+  native_metrics[FONT_METRICS_MAX_DESCENT] = native_metrics[FONT_METRICS_DESCENT];
+  native_metrics[FONT_METRICS_MAX_ADVANCE] = extents.max_x_advance;
+
+  (*env)->ReleaseDoubleArrayElements (env, java_metrics, native_metrics, 0);
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoShowGlyphs
-   (JNIEnv *env, jobject obj, jintArray java_codes, jfloatArray java_posns)
+static void
+paint_glyph_run(struct graphics2d *gr,
+               cairo_glyph_t **glyphs,
+               gint *n_glyphs,
+               PangoLayoutRun *run)
+{
+  gint i = 0;
+  gint x = 0, y = 0;
+
+  g_assert (gr != NULL);
+  g_assert (glyphs != NULL);
+  g_assert (n_glyphs != NULL);
+  g_assert (run != NULL);
+
+  if (run->glyphs != NULL && run->glyphs->num_glyphs > 0)
+    {
+      if (*n_glyphs < run->glyphs->num_glyphs)
+       {
+         *glyphs = g_realloc(*glyphs, 
+                             (sizeof(cairo_glyph_t) 
+                              * run->glyphs->num_glyphs));
+         *n_glyphs = run->glyphs->num_glyphs;
+       }
+      
+      g_assert (*glyphs != NULL);
+
+      if (gr->debug) printf ("painting %d glyphs: ", run->glyphs->num_glyphs);
+
+      for (i = 0; i < run->glyphs->num_glyphs; ++i)
+       {         
+         (*glyphs)[i].index = run->glyphs->glyphs[i].glyph;
+
+         (*glyphs)[i].x =
+           ((double) (x + run->glyphs->glyphs[i].geometry.x_offset)) 
+           / ((double) PANGO_SCALE);
+
+         (*glyphs)[i].y =
+           ((double) (y + run->glyphs->glyphs[i].geometry.y_offset)) 
+           / ((double) PANGO_SCALE);
+         
+         if (gr->debug) printf(" (%ld @ %f,%f)",  
+                               (*glyphs)[i].index,  
+                               (*glyphs)[i].x, 
+                               (*glyphs)[i].y);
+           
+         x += run->glyphs->glyphs[i].geometry.width;
+       }
+
+      if (gr->debug) printf("\n");
+      begin_drawing_operation (gr);
+      cairo_show_glyphs (gr->cr, *glyphs, run->glyphs->num_glyphs);
+      end_drawing_operation (gr);      
+    }
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoDrawString
+  (JNIEnv *env, jobject obj, jobject font, jstring str, float x, float y)
 {
   struct graphics2d *gr = NULL;
+  const char *cstr = NULL;
+  struct peerfont *pfont = NULL;
+
+  /*
   cairo_glyph_t *glyphs = NULL;
-  jfloat *native_posns = NULL;
-  jint *native_codes = NULL;
-  jint i;
-  jint ncodes, nposns;
+  gint n_glyphs = 0;
+  PangoLayoutRun *run = NULL;
+  PangoLayoutIter *iter = NULL;
+  */
 
-  gdk_threads_enter();
+  g_assert(obj != NULL);
+  g_assert(font != NULL);
+  g_assert(str != NULL);
+
+  gdk_threads_enter ();
   if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
-  g_assert (gr != NULL);
+  g_assert(gr != NULL);
 
-  native_codes = (*env)->GetIntArrayElements (env, java_codes, NULL);  
-  native_posns = (*env)->GetFloatArrayElements (env, java_posns, NULL);  
-  g_assert (native_codes != NULL);
-  g_assert (native_posns != NULL);
+  pfont = (struct peerfont *) NSA_GET_FONT_PTR (env, font);
+  g_assert (pfont != NULL);
+
+  cstr = (*env)->GetStringUTFChars (env, str, NULL);
+  g_assert(cstr != NULL);
 
-  ncodes = (*env)->GetArrayLength (env, java_codes);
-  nposns = (*env)->GetArrayLength (env, java_posns);
-  g_assert (2 * ncodes == nposns);
+  if (gr->debug) printf ("painting string '%s' at (%f,%f)\n", cstr, x, y);
 
-  if (gr->debug) printf ("cairo_show_glyphs (%d glyphs)\n", ncodes);
+  /* For now we let cairo do the glyph conversion; eventually this
+   * ought to be unified with pango, but it is impossible to get
+   * pango and cairo to agree on metrics at the moment, so we either
+   * have to use "all cairo" metrics (the string-based APIs) or 
+   * "all pango" metrics (the glyph-vector based APIs). 
+   */
 
-  glyphs = malloc (sizeof(cairo_glyph_t) * ncodes);
-  g_assert (glyphs);
+  install_font_peer (gr->cr, pfont, gr->debug);
+  cairo_move_to (gr->cr, x, y);
+  cairo_show_text (gr->cr, cstr);
+  
+  /*
+    
+  pango_layout_set_text (gr->pango_layout, cstr, -1);
 
-  for (i = 0; i < ncodes; ++i)
+  iter = pango_layout_get_iter (gr->pango_layout);
+  g_assert(iter != NULL);
+
+  cairo_translate (gr->cr, x, y);
+
+  do 
     {
-      glyphs[i].index = native_codes[i];
-      glyphs[i].x = (double) native_posns[2*i];
-      glyphs[i].y = (double) native_posns[2*i + 1];
-      if (gr->debug) printf ("cairo_show_glyphs (glyph %d (code %d) : %f,%f)\n", 
-                            i, glyphs[i].index, glyphs[i].x, glyphs[i].y);
-    }
+      run = pango_layout_iter_get_run (iter);
+      if (run != NULL)
+       paint_glyph_run (gr, &glyphs, &n_glyphs, run);
+    } 
+  while (pango_layout_iter_next_run (iter));
+  
+  if (glyphs != NULL)
+    g_free (glyphs);
 
-  (*env)->ReleaseIntArrayElements (env, java_codes, native_codes, 0);
-  (*env)->ReleaseFloatArrayElements (env, java_posns, native_posns, 0);
+  cairo_translate (gr->cr, -x, -y);
+  
+  pango_layout_iter_free (iter);
 
-  begin_drawing_operation (gr);
-  cairo_show_glyphs (gr->cr, glyphs, ncodes);
-  end_drawing_operation (gr);
+  */
 
-  free(glyphs);
-  gdk_threads_leave();
+  gdk_threads_leave ();
+
+  (*env)->ReleaseStringUTFChars (env, str, cstr);
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetOperator 
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoDrawGdkGlyphVector
+   (JNIEnv *env, jobject self, jobject font, jobject java_vec, jfloat x, jfloat y)
+{
+  
+  struct graphics2d *gr = NULL;
+  struct peerfont *pfont = NULL;
+  struct glyphvec *gv = NULL;
+  PangoLayoutRun *run = NULL;
+  cairo_glyph_t *glyphs = NULL;
+  gint n_glyphs = 0;
+
+  g_assert (self != NULL);
+  g_assert (java_vec != NULL);
+
+  gdk_threads_enter ();
+  if (peer_is_disposed(env, self)) { gdk_threads_leave(); return; }
+
+  gr = (struct graphics2d *)NSA_GET_G2D_PTR (env, self);
+  gv = (struct glyphvec *)NSA_GET_GV_PTR (env, java_vec);
+  pfont = (struct peerfont *) NSA_GET_FONT_PTR (env, font);
+
+  g_assert (gr != NULL);
+  g_assert (gv != NULL);
+  g_assert (pfont != NULL);
+
+  if (gr->debug) printf ("painting pango glyph vector\n");
+
+  install_font_peer (gr->cr, pfont, gr->debug);
+  cairo_translate (gr->cr, x, y);
+
+  /* nb. PangoLayoutRun is a typedef for PangoGlyphItem. */
+  run = (PangoLayoutRun *) gv->glyphitems;
+  if (run != NULL)
+    paint_glyph_run (gr, &glyphs, &n_glyphs, run);
+
+  if (glyphs != NULL)
+    g_free (glyphs);
+
+  cairo_translate (gr->cr, -x, -y);
+  gdk_threads_leave ();  
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoDrawGdkTextLayout
+   (JNIEnv *env, jobject self, jobject font, jobject java_layout, jfloat x, jfloat y)
+{
+  /* 
+   * FIXME: Some day we expect either cairo or pango will know how to make
+   * a pango layout paint to a cairo surface. that day is not yet here.
+   */
+
+  struct graphics2d *gr = NULL;
+  struct peerfont *pfont = NULL;
+  struct textlayout *tl = NULL;
+  PangoLayoutIter *i = NULL;
+  PangoLayoutRun *run = NULL;
+  cairo_glyph_t *glyphs = NULL;
+  gint n_glyphs = 0;
+
+  g_assert (self != NULL);
+  g_assert (java_layout != NULL);
+
+  gr = (struct graphics2d *)NSA_GET_G2D_PTR (env, self);
+  tl = (struct textlayout *)NSA_GET_TEXT_LAYOUT_PTR (env, java_layout);
+  pfont = (struct peerfont *) NSA_GET_FONT_PTR (env, font);
+
+  g_assert (gr != NULL);
+  g_assert (tl != NULL);
+  g_assert (tl->pango_layout != NULL);
+  g_assert (pfont != NULL);
+
+  if (gr->debug) printf ("painting pango layout\n");
+
+  gdk_threads_enter ();
+  if (peer_is_disposed(env, self)) { gdk_threads_leave(); return; }
+
+  i = pango_layout_get_iter (tl->pango_layout);
+  g_assert (i != NULL);
+
+  install_font_peer (gr->cr, pfont, gr->debug);
+  cairo_translate (gr->cr, x, y);
+
+  do 
+    {
+      run = pango_layout_iter_get_run (i);
+      if (run != NULL)
+       paint_glyph_run (gr, &glyphs, &n_glyphs, run);
+    } 
+  while (pango_layout_iter_next_run (i));
+  
+  if (glyphs != NULL)
+    g_free (glyphs);
+
+  cairo_translate (gr->cr, -x, -y);
+  
+  pango_layout_iter_free (i);
+  gdk_threads_leave ();
+}
+
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetOperator 
    (JNIEnv *env, jobject obj, jint op)
 {
   struct graphics2d *gr = NULL;
@@ -1055,7 +1337,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetOperator
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetRGBColor 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetRGBColor 
    (JNIEnv *env, jobject obj, jdouble r, jdouble g, jdouble b)
 {
   struct graphics2d *gr = NULL;
@@ -1081,7 +1364,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetRGBColor
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetAlpha 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetAlpha 
    (JNIEnv *env, jobject obj, jdouble a)
 {
   struct graphics2d *gr = NULL;
@@ -1091,12 +1375,13 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetAlpha
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
   g_assert (gr != NULL);
-  if (gr->debug) printf ("cairo_set_alpha %f\n", a);
+  if (gr->debug) printf ("cairo_set_alpha %f\n", a);  
   cairo_set_alpha (gr->cr, a);
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetFillRule 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetFillRule 
    (JNIEnv *env, jobject obj, jint rule)
 {
   struct graphics2d *gr = NULL;
@@ -1119,7 +1404,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetFillRule
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineWidth 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineWidth 
    (JNIEnv *env, jobject obj, jdouble width)
 {
   struct graphics2d *gr = NULL;
@@ -1134,7 +1420,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineWidt
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineCap 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineCap 
    (JNIEnv *env, jobject obj, jint cap)
 {
   struct graphics2d *gr = NULL;
@@ -1162,7 +1449,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineCap
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineJoin 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineJoin 
    (JNIEnv *env, jobject obj, jint join)
 {
   struct graphics2d *gr = NULL;
@@ -1190,7 +1478,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetLineJoin
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetDash 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetDash 
    (JNIEnv *env, jobject obj, jdoubleArray dashes, jint ndash, jdouble offset)
 {
   struct graphics2d *gr = NULL;
@@ -1209,7 +1498,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetDash
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMiterLimit 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMiterLimit 
    (JNIEnv *env, jobject obj, jdouble miter)
 {
   struct graphics2d *gr = NULL;
@@ -1225,7 +1515,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSetMiterLim
 }
 
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoNewPath 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoNewPath 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -1234,13 +1525,14 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoNewPath
   if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
-  g_assert (gr != NULL);
+  if (gr == NULL) { gdk_threads_leave (); return; }
   if (gr->debug) printf ("cairo_new_path\n");
   cairo_new_path (gr->cr);
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoMoveTo 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoMoveTo 
    (JNIEnv *env, jobject obj, jdouble x, jdouble y)
 {
   struct graphics2d *gr = NULL;
@@ -1255,7 +1547,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoMoveTo
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoLineTo 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoLineTo 
    (JNIEnv *env, jobject obj, jdouble x, jdouble y)
 {
   struct graphics2d *gr = NULL;
@@ -1270,7 +1563,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoLineTo
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoCurveTo 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoCurveTo 
    (JNIEnv *env, jobject obj, jdouble x1, jdouble y1, jdouble x2, jdouble y2, jdouble x3, jdouble y3)
 {
   struct graphics2d *gr = NULL;
@@ -1285,7 +1579,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoCurveTo
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelMoveTo 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelMoveTo 
    (JNIEnv *env, jobject obj, jdouble dx, jdouble dy)
 {
   struct graphics2d *gr = NULL;
@@ -1300,7 +1595,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelMoveTo
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelLineTo 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelLineTo 
    (JNIEnv *env, jobject obj, jdouble dx, jdouble dy)
 {
   struct graphics2d *gr = NULL;
@@ -1315,7 +1611,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelLineTo
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelCurveTo 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelCurveTo 
    (JNIEnv *env, jobject obj, jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2, jdouble dx3, jdouble dy3)
 {
   struct graphics2d *gr = NULL;
@@ -1330,7 +1627,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRelCurveTo
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRectangle 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRectangle 
    (JNIEnv *env, jobject obj, jdouble x, jdouble y, jdouble width, jdouble height)
 {
   struct graphics2d *gr = NULL;
@@ -1339,13 +1637,14 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoRectangle
   if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
-  g_assert (gr != NULL);
+  if (gr == NULL) { gdk_threads_leave (); return; }
   if (gr->debug) printf ("cairo_rectangle (%f, %f) (%f, %f)\n", x, y, width, height);
   cairo_rectangle (gr->cr, x, y, width, height);
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClosePath 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClosePath 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -1360,7 +1659,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClosePath
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoStroke 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoStroke 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -1377,7 +1677,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoStroke
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoFill 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoFill 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -1394,7 +1695,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoFill
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClip 
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClip 
    (JNIEnv *env, jobject obj)
 {
   struct graphics2d *gr = NULL;
@@ -1403,7 +1705,7 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClip
   if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }
 
   gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
-  g_assert (gr != NULL);
+  if (gr == NULL) { gdk_threads_leave (); return; }
   if (gr->debug) printf ("cairo_clip\n");
   begin_drawing_operation (gr);
   cairo_init_clip (gr->cr);
@@ -1412,7 +1714,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoClip
   gdk_threads_leave();
 }
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSurfaceSetFilter
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoSurfaceSetFilter
    (JNIEnv *env, jobject obj, jint filter)
 {
    struct graphics2d *gr = NULL;