Index: modules/arabic/arabic-fc.c
===================================================================
RCS file: /cvs/gnome/pango/modules/arabic/arabic-fc.c,v
retrieving revision 1.12
diff -u -p -r1.12 arabic-fc.c
--- modules/arabic/arabic-fc.c	3 Aug 2003 21:57:26 -0000	1.12
+++ modules/arabic/arabic-fc.c	25 Aug 2003 13:28:40 -0000
@@ -50,11 +50,11 @@ static PangoEngineInfo script_engines[] 
 };
 
 static void
-maybe_add_feature (PangoOTRuleset *ruleset,
-		   PangoOTInfo    *info,
-		   guint           script_index,
-		   PangoOTTag      tag,
-		   gulong          property_bit)
+maybe_add_gsub_feature (PangoOTRuleset *ruleset,
+			PangoOTInfo    *info,
+			guint           script_index,
+			PangoOTTag      tag,
+			gulong          property_bit)
 {
   guint feature_index;
   
@@ -65,6 +65,22 @@ maybe_add_feature (PangoOTRuleset *rules
 				  property_bit);
 }
 
+static void
+maybe_add_gpos_feature (PangoOTRuleset *ruleset,
+			PangoOTInfo    *info,
+			guint           script_index,
+			PangoOTTag      tag,
+			gulong          property_bit)
+{
+  guint feature_index;
+  
+  /* 0xffff == default language system */
+  if (pango_ot_info_find_feature (info, PANGO_OT_TABLE_GPOS,
+				  tag, script_index, 0xffff, &feature_index))
+    pango_ot_ruleset_add_feature (ruleset, PANGO_OT_TABLE_GPOS, feature_index,
+				  property_bit);
+}
+
 static PangoOTRuleset *
 get_ruleset (FT_Face face)
 {
@@ -91,11 +107,24 @@ get_ruleset (FT_Face face)
       if (pango_ot_info_find_script (info, PANGO_OT_TABLE_GSUB,
 				     arab_tag, &script_index))
 	{
-	  maybe_add_feature (ruleset, info, script_index, FT_MAKE_TAG ('i','s','o','l'), isolated);
-	  maybe_add_feature (ruleset, info, script_index, FT_MAKE_TAG ('i','n','i','t'), initial);
-	  maybe_add_feature (ruleset, info, script_index, FT_MAKE_TAG ('m','e','d','i'), medial);
-	  maybe_add_feature (ruleset, info, script_index, FT_MAKE_TAG ('f','i','n','a'), final);
-	  maybe_add_feature (ruleset, info, script_index, FT_MAKE_TAG ('l','i','g','a'), 0xFFFF);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('c','c','m','p'), 0xFFFF);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('i','s','o','l'), isolated);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('f','i','n','a'), final);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('m','e','d','i'), medial);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('i','n','i','t'), initial);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('r','l','i','g'), 0xFFFF);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('c','a','l','t'), 0xFFFF);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('l','i','g','a'), 0xFFFF);
+	  maybe_add_gsub_feature (ruleset, info, script_index, FT_MAKE_TAG ('m','s','e','t'), 0xFFFF);
+	}
+
+      if (pango_ot_info_find_script (info, PANGO_OT_TABLE_GPOS,
+				     arab_tag, &script_index))
+	{
+	  maybe_add_gpos_feature (ruleset, info, script_index, FT_MAKE_TAG ('c','u','r','s'), 0xFFFF);
+	  maybe_add_gpos_feature (ruleset, info, script_index, FT_MAKE_TAG ('k','e','r','n'), 0xFFFF);
+	  maybe_add_gpos_feature (ruleset, info, script_index, FT_MAKE_TAG ('m','a','r','k'), 0xFFFF);
+	  maybe_add_gpos_feature (ruleset, info, script_index, FT_MAKE_TAG ('m','k','m','k'), 0xFFFF);
 	}
 
       g_object_set_qdata_full (G_OBJECT (info), ruleset_quark, ruleset,
@@ -198,6 +227,7 @@ arabic_engine_shape (PangoEngineShape *e
 	}
       else
 	{
+#if 0	  
 	  /* Hack - Microsoft fonts are strange and don't contain the
 	   * correct rules to shape ARABIC LETTER FARSI YEH in
 	   * medial/initial position. It looks identical to ARABIC LETTER
@@ -206,6 +236,7 @@ arabic_engine_shape (PangoEngineShape *e
 	  if (wc == 0x6cc && ruleset &&
 	      ((properties[i] & (initial | medial)) != (initial | medial)))
 	    wc = 0x64a;
+#endif	  
 	  
 	  index = pango_fc_font_get_glyph (fc_font, wc);
 
@@ -221,23 +252,7 @@ arabic_engine_shape (PangoEngineShape *e
 	      if (g_unichar_type (wc) == G_UNICODE_NON_SPACING_MARK)
 		{
 		  if (i > 0)
-		    {
-		      glyphs->log_clusters[i] = glyphs->log_clusters[i-1];
-#if 0		      
-		      PangoRectangle logical_rect, ink_rect;
-		      
-		      glyphs->glyphs[i].geometry.width = MAX (glyphs->glyphs[i-1].geometry.width,
-							      glyphs->glyphs[i].geometry.width);
-		      glyphs->glyphs[i-1].geometry.width = 0;
-		      
-		      /* Some heuristics to try to guess how overstrike glyphs are
-		       * done and compensate
-		       */
-		      pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, &ink_rect, &logical_rect);
-		      if (logical_rect.width == 0 && ink_rect.x == 0)
-			glyphs->glyphs[i].geometry.x_offset = (glyphs->glyphs[i].geometry.width - ink_rect.width) / 2;
-#endif
-		    }
+		    glyphs->log_clusters[i] = glyphs->log_clusters[i-1];
 		}
 	    }
 	}
@@ -249,50 +264,37 @@ arabic_engine_shape (PangoEngineShape *e
 
   if (ruleset)
     {
-      pango_ot_ruleset_shape (ruleset, glyphs, properties);
+      pango_ot_ruleset_substitute (ruleset, glyphs, properties);
+
+      pango_ot_ruleset_position (ruleset, font, glyphs, properties,
+				 analysis->level % 2 != 0);
 
       g_free (wcs);
       g_free (properties);
-
     }
-
-  for (i = 0; i < glyphs->num_glyphs; i++)
+  else
     {
-
-      if (glyphs->glyphs[i].glyph)
+      /* Apply default positioning */
+      for (i = 0; i < glyphs->num_glyphs; i++)
 	{
-	  PangoRectangle logical_rect;
+	  if (glyphs->glyphs[i].glyph)
+	    {
+	      PangoRectangle logical_rect;
+	      
+	      pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
+	      glyphs->glyphs[i].geometry.width = logical_rect.width;
+	    }
+	  else
+	    glyphs->glyphs[i].geometry.width = 0;
 	  
-	  pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
-	  glyphs->glyphs[i].geometry.width = logical_rect.width;
+	  glyphs->glyphs[i].geometry.x_offset = 0;
+	  glyphs->glyphs[i].geometry.y_offset = 0;
 	}
-      else
-	glyphs->glyphs[i].geometry.width = 0;
-  
-      glyphs->glyphs[i].geometry.x_offset = 0;
-      glyphs->glyphs[i].geometry.y_offset = 0;
-    }
-
-  /* Simple bidi support */
 
-  if (analysis->level % 2)
-    {
-      int start, end;
-
-      /* Swap all glyphs */
-      swap_range (glyphs, 0, glyphs->num_glyphs);
-      
-      /* Now reorder glyphs within each cluster back to LTR */
-      for (start=0; start<glyphs->num_glyphs;)
+      if (analysis->level % 2 != 0)
 	{
-	  end = start;
-	  while (end < glyphs->num_glyphs &&
-		 glyphs->log_clusters[end] == glyphs->log_clusters[start])
-	    end++;
-
-	  if (end > start + 1)
-	    swap_range (glyphs, start, end);
-	  start = end;
+	  /* Swap all glyphs */
+	  swap_range (glyphs, 0, glyphs->num_glyphs);
 	}
     }
   
Index: modules/arabic/arabic-ot.c
===================================================================
RCS file: /cvs/gnome/pango/modules/arabic/arabic-ot.c,v
retrieving revision 1.4
diff -u -p -r1.4 arabic-ot.c
--- modules/arabic/arabic-ot.c	20 Aug 2002 19:51:35 -0000	1.4
+++ modules/arabic/arabic-ot.c	25 Aug 2003 13:28:40 -0000
@@ -54,8 +54,8 @@ joining_class  arabic[] =
   transparent, transparent, transparent, transparent,
 
   /* U+0650 */
-  transparent, transparent, transparent, none,
-  none, none, none, none,
+  transparent, transparent, transparent, transparent,
+  transparent, transparent, transparent, transparent,
   none, none, none, none,
   none, none, none, none,
 
Index: pango/pango-ot.h
===================================================================
RCS file: /cvs/gnome/pango/pango/pango-ot.h,v
retrieving revision 1.4
diff -u -p -r1.4 pango-ot.h
--- pango/pango-ot.h	4 Jun 2002 00:20:47 -0000	1.4
+++ pango/pango-ot.h	25 Aug 2003 13:28:40 -0000
@@ -24,6 +24,7 @@
 
 #include <freetype/freetype.h>
 #include <pango/pango-glyph.h>
+#include <pango/pango-font.h>
 
 G_BEGIN_DECLS
 
@@ -80,6 +81,14 @@ void            pango_ot_ruleset_add_fea
 void            pango_ot_ruleset_shape       (PangoOTRuleset   *ruleset,
 					      PangoGlyphString *glyphs,
 					      gulong           *properties);
+void            pango_ot_ruleset_substitute  (PangoOTRuleset   *ruleset,
+					      PangoGlyphString *glyphs,
+					      gulong           *properties);
+void            pango_ot_ruleset_position    (PangoOTRuleset   *ruleset,
+					      PangoFont        *font,
+					      PangoGlyphString *glyphs,
+					      gulong           *properties,
+					      gboolean          rtl);
 
 #endif /* PANGO_ENABLE_ENGINE */
 
Index: pango/opentype/ftxgdef.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxgdef.c,v
retrieving revision 1.9
diff -u -p -r1.9 ftxgdef.c
--- pango/opentype/ftxgdef.c	29 Jul 2003 14:21:54 -0000	1.9
+++ pango/opentype/ftxgdef.c	25 Aug 2003 13:28:40 -0000
@@ -759,7 +759,7 @@
 
     if ( glyphID < gcrr[index].Start )
     {
-      array_index = 0;
+      array_index = index;
       if ( index == 0 )
         glyph_index = glyphID;
       else
@@ -1125,7 +1125,7 @@
 
     if ( glyphID < gcrr[index].Start )
     {
-      array_index = 0;
+      array_index = index;
       if ( index == 0 )
         glyph_index = glyphID;
       else
Index: pango/opentype/ftxgpos.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxgpos.c,v
retrieving revision 1.17
diff -u -p -r1.17 ftxgpos.c
--- pango/opentype/ftxgpos.c	27 Jul 2003 02:40:31 -0000	1.17
+++ pango/opentype/ftxgpos.c	25 Aug 2003 13:28:40 -0000
@@ -43,8 +43,6 @@
     FT_UShort        load_flags;  /* how the glyph should be loaded */
     FT_Bool          r2l;
 
-    FT_UShort        first;       /* the first glyph in a chain of
-                                     cursive connections           */
     FT_UShort        last;        /* the last valid glyph -- used
                                      with cursive positioning     */
     FT_Pos           anchor_x;    /* the coordinates of the anchor point */
@@ -2082,8 +2080,17 @@
       out[gpi->last].new_advance = TRUE;
     }
 
-    out[in->pos].y_pos = gpi->anchor_y - entry_y + out[gpi->last].y_pos;
-
+    if ( flags & RIGHT_TO_LEFT )
+      {
+	out[gpi->last].cursive_chain = gpi->last - in->pos;
+	out[gpi->last].y_pos = entry_y - gpi->anchor_y;
+      }
+    else
+      {
+	out[in->pos].cursive_chain = in->pos - gpi->last;
+	out[in->pos].y_pos = gpi->anchor_y - entry_y;
+      }
+    
   end:
     error = Get_Anchor( gpi, &eer->ExitAnchor, in->string[in->pos],
                         &exit_x, &exit_y );
@@ -2091,8 +2098,6 @@
       gpi->last = 0xFFFF;
     else
     {
-      if ( gpi->first == 0xFFFF )
-        gpi->first  = in->pos;
       gpi->last     = in->pos;
       gpi->anchor_x = exit_x;
       gpi->anchor_y = exit_y;
@@ -6134,11 +6139,8 @@
     FT_UShort*  p_in       = in->properties;
 
     int       nesting_level = 0;
-    FT_UShort i;
-    FT_Pos    offset;
 
 
-    gpi->first = 0xFFFF;
     gpi->last  = 0xFFFF;     /* no last valid glyph for cursive pos. */
 
     in->pos = 0;
@@ -6170,22 +6172,6 @@
         error = TTO_Err_Not_Covered;
       }
 
-      /* test whether we have to adjust the offsets for cursive connections */
-
-      if ( gpi->first != 0xFFFF && gpi->last == 0xFFFF &&
-           gpos->LookupList.Lookup[lookup_index].LookupFlag & RIGHT_TO_LEFT )
-      {
-        offset = out[in->pos].y_pos;
-
-        /* no horizontal offsets (for vertical writing direction)
-           supported yet                                          */
-
-        for ( i = gpi->first; i <= in->pos; i++ )
-          out[i].y_pos -= offset;
-
-        gpi->first = 0xFFFF;
-      }
-
       if ( error == TTO_Err_Not_Covered )
         (in->pos)++;
       else
@@ -6196,6 +6182,30 @@
   }
 
 
+  static FT_Error  Position_CursiveChain ( TTO_GPOS_Data* out,
+					   FT_ULong       length )
+  {
+    FT_ULong   i, j;
+
+    /* First handle all left-to-right connections */
+    for (j = 0; j < length; j--)
+    {
+      if (out[j].cursive_chain > 0)
+	out[j].y_pos += out[j - out[j].cursive_chain].y_pos;
+    }
+    
+    /* Then handle all right-to-left connections */
+    for (i = length; i > 0; i--)
+    {
+      j = i - 1;
+
+      if (out[j].cursive_chain < 0)
+	out[j].y_pos += out[j - out[j].cursive_chain].y_pos;
+    }
+    
+    return TT_Err_Ok;
+  }
+
   EXPORT_FUNC
   FT_Error  TT_GPOS_Add_Feature( TTO_GPOSHeader*  gpos,
                                  FT_UShort        feature_index,
@@ -6271,7 +6281,6 @@
     return TT_Err_Ok;
   }
 
-
   /* If `dvi' is TRUE, glyph contour points for anchor points and device
      tables are ignored -- you will get device independent values.         */
 
@@ -6322,6 +6331,10 @@
 	else
 	  retError = error;
       }
+
+    error = Position_CursiveChain ( *out, in->length );
+    if ( error )
+      return error;
 
     return retError;
   }
Index: pango/opentype/ftxgpos.h
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxgpos.h,v
retrieving revision 1.2
diff -u -p -r1.2 ftxgpos.h
--- pango/opentype/ftxgpos.h	26 Jul 2003 03:03:40 -0000	1.2
+++ pango/opentype/ftxgpos.h	25 Aug 2003 13:28:40 -0000
@@ -773,6 +773,9 @@ extern "C" {
                                    absolute, i.e., they won't be
                                    added to the original glyph's value
                                    but rather replace them.            */
+    FT_Short  cursive_chain;   /* character to which this connects,
+				   may be positive or negative; used
+				   only internally                     */
   };
 
   typedef struct TTO_GPOS_Data_  TTO_GPOS_Data;
Index: pango/opentype/ftxgsub.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxgsub.c,v
retrieving revision 1.15
diff -u -p -r1.15 ftxgsub.c
--- pango/opentype/ftxgsub.c	28 Jul 2003 22:28:37 -0000	1.15
+++ pango/opentype/ftxgsub.c	25 Aug 2003 13:28:40 -0000
@@ -3811,9 +3811,7 @@
     s_in     = &in->string[curr_pos];
     ic       = ccsf3->InputCoverage;
 
-    /* Start at 1 because [0] is implied */
-
-    for ( i = 1, j = 1; i < igc; i++, j++ )
+    for ( i = 0, j = 0; i < igc; i++, j++ )
     {
       while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
       {
Index: pango/opentype/pango-ot-info.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/pango-ot-info.c,v
retrieving revision 1.8
diff -u -p -r1.8 pango-ot-info.c
--- pango/opentype/pango-ot-info.c	16 Apr 2003 21:48:29 -0000	1.8
+++ pango/opentype/pango-ot-info.c	25 Aug 2003 13:28:40 -0000
@@ -161,17 +161,31 @@ compare_glyph_info (gconstpointer a,
 /* Make a guess at the appropriate class for a glyph given 
  * a character code that maps to the glyph
  */
-static FT_UShort
-get_glyph_class (gunichar charcode)
+static gboolean
+get_glyph_class (gunichar   charcode,
+		 FT_UShort *class)
 {
+  /* For characters mapped into the Arabic Presentation forms, using properties
+   * derived as we apply GSUB substitutions will be more reliable
+   */
+  if ((charcode >= 0xFB50 && charcode <= 0xFDFF) || /* Arabic Presentation Forms-A */
+      (charcode >= 0xFE70 && charcode <= 0XFEFF))   /* Arabic Presentation Forms-B */
+    return FALSE;
+  
   switch (g_unichar_type (charcode))
     {
     case G_UNICODE_COMBINING_MARK:
     case G_UNICODE_ENCLOSING_MARK:
     case G_UNICODE_NON_SPACING_MARK:
-      return 3;		/* Mark glyph (non-spacing combining glyph) */
+      *class = 3;		/* Mark glyph (non-spacing combining glyph) */
+      return TRUE;
+    case G_UNICODE_UNASSIGNED:
+    case G_UNICODE_PRIVATE_USE:
+      return FALSE;		/* Unknown, don't assign a class; classes get
+				 * propagated during GSUB application */
     default:
-      return 1;		/* Base glyph (single character, spacing glyph) */
+      *class = 1;               /* Base glyph (single character, spacing glyph) */
+      return TRUE;
     }
 }
 
@@ -224,9 +238,8 @@ synthesize_class_def (PangoOTInfo *info)
       if (glyph <= 65535)
 	{
 	  glyph_info.glyph = glyph;
-	  glyph_info.class = get_glyph_class (charcode);
-	  
-	  g_array_append_val (glyph_infos, glyph_info);
+	  if (get_glyph_class (charcode, &glyph_info.class))
+	    g_array_append_val (glyph_infos, glyph_info);
 	}
       
       charcode = FT_Get_Next_Char (info->face, charcode, &glyph);
Index: pango/opentype/pango-ot-ruleset.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/pango-ot-ruleset.c,v
retrieving revision 1.7
diff -u -p -r1.7 pango-ot-ruleset.c
--- pango/opentype/pango-ot-ruleset.c	24 Jul 2003 21:05:29 -0000	1.7
+++ pango/opentype/pango-ot-ruleset.c	25 Aug 2003 13:28:40 -0000
@@ -139,6 +139,32 @@ pango_ot_ruleset_add_feature (PangoOTRul
   g_array_append_val (ruleset->rules, tmp_rule);
 }
 
+static gboolean
+suppress_gsub (void)
+{
+  static int val = -1;
+  if (val < 0)
+    {
+      const char *v = g_getenv ("PANGO_NO_GSUB");
+      val = (v && v[0]);
+    }
+
+  return val;
+}
+
+static gboolean
+suppress_gpos (void)
+{
+  static int val = -1;
+  if (val < 0)
+    {
+      const char *v = g_getenv ("PANGO_NO_GPOS");
+      val = (v && v[0]);
+    }
+
+  return val;
+}
+
 /**
  * pango_ot_ruleset_shape:
  * @ruleset: a #PangoOTRuleset.
@@ -188,7 +214,7 @@ pango_ot_ruleset_shape (PangoOTRuleset  
 	TT_GSUB_Clear_Features (gsub);
     }
 
-  if (need_gpos)
+  if (need_gpos && !suppress_gpos ())
     {
       gpos = pango_ot_info_get_gpos (ruleset->info);
 
@@ -299,4 +325,305 @@ pango_ot_ruleset_shape (PangoOTRuleset  
     TT_GSUB_String_Done (in_string);
   if (out_string)
     TT_GSUB_String_Done (out_string);
+}
+
+
+void
+pango_ot_ruleset_substitute  (PangoOTRuleset   *ruleset,
+			      PangoGlyphString *glyphs,
+			      gulong           *properties)
+{
+  int i;
+  int last_cluster;
+  int result;
+  
+  TTO_GSUB gsub = NULL;
+  
+  TTO_GSUB_String *in_string = NULL;
+  TTO_GSUB_String *out_string = NULL;
+
+  gboolean need_gsub = FALSE;
+
+  g_return_if_fail (PANGO_OT_IS_RULESET (ruleset));
+
+  for (i = 0; i < ruleset->rules->len; i++)
+    {
+      PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+      if (rule->table_type == PANGO_OT_TABLE_GSUB)
+	need_gsub = TRUE;
+    }
+
+  if (need_gsub && !suppress_gsub ())
+    {
+      gsub = pango_ot_info_get_gsub (ruleset->info);
+
+      if (gsub)
+	TT_GSUB_Clear_Features (gsub);
+    }
+
+  for (i = 0; i < ruleset->rules->len; i++)
+    {
+      PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+      if (rule->table_type == PANGO_OT_TABLE_GSUB)
+	{
+	  if (gsub)
+	    TT_GSUB_Add_Feature (gsub, rule->feature_index, rule->property_bit);
+	}
+    }
+
+  if (!gsub)
+    return;
+
+  result = TT_GSUB_String_New (ruleset->info->face->memory, &in_string);
+  g_assert (result == FT_Err_Ok);
+
+  result = TT_GSUB_String_Set_Length (in_string, glyphs->num_glyphs);
+  g_assert (result == FT_Err_Ok);
+
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      in_string->string[i] = glyphs->glyphs[i].glyph;
+      in_string->properties[i] = properties[i];
+      in_string->logClusters[i] = glyphs->log_clusters[i];
+    }
+  in_string->max_ligID = i;
+  
+  result = TT_GSUB_String_New (ruleset->info->face->memory,
+			       &out_string);
+  g_assert (result == FT_Err_Ok);
+
+  TT_GSUB_Apply_String (gsub, in_string, out_string);
+
+  pango_glyph_string_set_size (glyphs, out_string->length);
+
+  last_cluster = -1;
+  for (i = 0; i < out_string->length; i++)
+    {
+      glyphs->glyphs[i].glyph = out_string->string[i];
+
+      glyphs->log_clusters[i] = out_string->logClusters[i];
+      if (glyphs->log_clusters[i] != last_cluster)
+	glyphs->glyphs[i].attr.is_cluster_start = 1;
+      else
+	glyphs->glyphs[i].attr.is_cluster_start = 0;
+
+      last_cluster = glyphs->log_clusters[i];
+    }
+
+  if (in_string)
+    TT_GSUB_String_Done (in_string);
+  if (out_string)
+    TT_GSUB_String_Done (out_string);
+}
+
+static void
+swap_range (PangoGlyphString *glyphs, int start, int end)
+{
+  int i, j;
+  
+  for (i = start, j = end - 1; i < j; i++, j--)
+    {
+      PangoGlyphInfo glyph_info;
+      gint log_cluster;
+      
+      glyph_info = glyphs->glyphs[i];
+      glyphs->glyphs[i] = glyphs->glyphs[j];
+      glyphs->glyphs[j] = glyph_info;
+      
+      log_cluster = glyphs->log_clusters[i];
+      glyphs->log_clusters[i] = glyphs->log_clusters[j];
+      glyphs->log_clusters[j] = log_cluster;
+    }
+}
+
+static void
+apply_gpos_ltr (PangoOTRuleset   *ruleset,
+		PangoGlyphString *glyphs,
+		TTO_GPOS_Data    *outgpos)
+{
+  int i;
+	    
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      FT_Pos x_pos = outgpos[i].x_pos;
+      FT_Pos y_pos = outgpos[i].y_pos;
+      int back = i;
+      int j;
+      
+      while (outgpos[back].back != 0)
+	{
+	  back  -= outgpos[back].back;
+	  x_pos += outgpos[back].x_pos;
+	  y_pos += outgpos[back].y_pos;
+	}
+      
+      for (j = back; j < i; j++)
+	glyphs->glyphs[i].geometry.x_offset -= glyphs->glyphs[j].geometry.width;
+      
+      glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6(x_pos);
+      glyphs->glyphs[i].geometry.y_offset += PANGO_UNITS_26_6(y_pos);
+      
+      if (outgpos[i].new_advance)
+	glyphs->glyphs[i].geometry.width  = PANGO_UNITS_26_6(outgpos[i].x_advance);
+      else
+	glyphs->glyphs[i].geometry.width += PANGO_UNITS_26_6(outgpos[i].x_advance);
+    }
+}
+
+static void
+apply_gpos_rtl (PangoOTRuleset   *ruleset,
+		PangoGlyphString *glyphs,
+		TTO_GPOS_Data    *outgpos)
+{
+  int i;
+  
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      int i_rev = glyphs->num_glyphs - i - 1;
+      int back_rev = i_rev;
+      int back;
+      FT_Pos x_pos = outgpos[i_rev].x_pos;
+      FT_Pos y_pos = outgpos[i_rev].y_pos;
+      int j;
+
+      while (outgpos[back_rev].back != 0)
+	{
+	  back_rev -= outgpos[back_rev].back;
+	  x_pos += outgpos[back_rev].x_pos;
+	  y_pos += outgpos[back_rev].y_pos;
+	}
+
+      back = glyphs->num_glyphs - back_rev - 1;
+      
+      for (j = i; j < back; j++)
+	glyphs->glyphs[i].geometry.x_offset += glyphs->glyphs[j].geometry.width;
+      
+      glyphs->glyphs[i].geometry.x_offset += PANGO_UNITS_26_6(x_pos);
+      glyphs->glyphs[i].geometry.y_offset -= PANGO_UNITS_26_6(y_pos);
+      
+      if (outgpos[i_rev].new_advance)
+	glyphs->glyphs[i].geometry.width  = PANGO_UNITS_26_6(outgpos[i_rev].x_advance);
+      else
+	glyphs->glyphs[i].geometry.width += PANGO_UNITS_26_6(outgpos[i_rev].x_advance);
+    }
+}
+
+void
+pango_ot_ruleset_position (PangoOTRuleset   *ruleset,
+			   PangoFont        *font,
+			   PangoGlyphString *glyphs,
+			   gulong           *properties,
+			   gboolean          rtl)
+{
+  int i;
+  int result;
+  
+  TTO_GPOS gpos = NULL;
+  TTO_GDEF gdef = NULL;
+  
+  TTO_GSUB_String *in_string = NULL;
+
+  gboolean need_gpos = FALSE;
+
+  g_return_if_fail (PANGO_OT_IS_RULESET (ruleset));
+
+  for (i = 0; i < ruleset->rules->len; i++)
+    {
+      PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+      if (rule->table_type == PANGO_OT_TABLE_GPOS)
+	need_gpos = TRUE;
+    }
+
+  if (need_gpos && !suppress_gpos ())
+    gpos = pango_ot_info_get_gpos (ruleset->info);
+
+  gdef = pango_ot_info_get_gdef (ruleset->info);
+  
+  if (gpos)
+    {
+      TT_GPOS_Clear_Features (gpos);
+
+      for (i = 0; i < ruleset->rules->len; i++)
+	{
+	  PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+	  
+	  if (rule->table_type == PANGO_OT_TABLE_GPOS)
+	    {
+	      if (gpos)
+		TT_GPOS_Add_Feature (gpos, rule->feature_index, rule->property_bit);
+	    }
+	}
+
+      result = TT_GSUB_String_New (ruleset->info->face->memory, &in_string);
+      g_assert (result == FT_Err_Ok);
+
+      result = TT_GSUB_String_Set_Length (in_string, glyphs->num_glyphs);
+      g_assert (result == FT_Err_Ok);
+
+      for (i = 0; i < glyphs->num_glyphs; i++)
+	{
+	  in_string->string[i] = glyphs->glyphs[i].glyph;
+	  in_string->properties[i] = properties[i];
+	  in_string->logClusters[i] = glyphs->log_clusters[i];
+	}
+      in_string->max_ligID = i;
+    }
+
+  /* Apply default positioning */
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      if (glyphs->glyphs[i].glyph)
+	{
+	  PangoRectangle logical_rect;
+	  
+	  FT_UShort property;
+
+	  if (gdef &&
+	      TT_GDEF_Get_Glyph_Property (gdef, glyphs->glyphs[i].glyph, &property) == FT_Err_Ok &&
+	      (property == TTO_MARK || (property & IGNORE_SPECIAL_MARKS) != 0))
+	    {
+	      glyphs->glyphs[i].geometry.width = 0;
+	    }
+	  else
+	    {
+	      pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
+	      glyphs->glyphs[i].geometry.width = logical_rect.width;
+	    }
+	}
+      else
+	glyphs->glyphs[i].geometry.width = 0;
+  
+      glyphs->glyphs[i].geometry.x_offset = 0;
+      glyphs->glyphs[i].geometry.y_offset = 0;
+    }
+
+  if (rtl)
+    {
+      /* Swap all glyphs */
+      swap_range (glyphs, 0, glyphs->num_glyphs);
+    }
+
+  /* Apply GPOS rules */
+  if (gpos)
+    {
+      TTO_GPOS_Data *outgpos = NULL;
+
+      if (TT_GPOS_Apply_String (ruleset->info->face, gpos, 0, in_string, &outgpos,
+				FALSE /* enable device-dependant values */,
+				rtl) == FT_Err_Ok)
+	{
+	  if (rtl)
+	    apply_gpos_rtl (ruleset, glyphs, outgpos);
+	  else
+	    apply_gpos_ltr (ruleset, glyphs, outgpos);
+
+	  FT_Free(gpos->memory, (void *)outgpos);
+	}
+    }
+
+  if (in_string)
+    TT_GSUB_String_Done (in_string);
 }
