Index: ftxgsub.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxgsub.c,v
retrieving revision 1.27
diff -u -p -r1.27 ftxgsub.c
--- ftxgsub.c	22 Sep 2004 18:14:55 -0000	1.27
+++ ftxgsub.c	15 Dec 2004 01:21:18 -0000
@@ -3641,7 +3641,288 @@
   }
 
 
+  FT_Error  Load_ReverseChainContextSubst( TTO_ReverseChainContextSubst*  rccs,
+					   FT_Stream                      stream )
+  {
+    FT_Error error;
+    FT_Memory memory = stream->memory;
+
+    FT_UShort               m, count;
+
+    FT_UShort               nb = 0, nl = 0, n;
+    FT_UShort               backtrack_count, lookahead_count;
+    FT_ULong                cur_offset, new_offset, base_offset;
+
+    TTO_Coverage*           b;
+    TTO_Coverage*           l;
+    FT_UShort*              sub;
+
+    base_offset = FILE_Pos();
+
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+    
+    rccs->SubstFormat = GET_UShort();
+    
+    if ( rccs->SubstFormat != 1 )
+      return TTO_Err_Invalid_GSUB_SubTable_Format;
+
+    FORGET_Frame();   
+
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+    
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+         ( error = Load_Coverage( &rccs->Coverage, stream ) ) != TT_Err_Ok )
+      return error;
+    (void)FILE_Seek( cur_offset );
+
+    
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail4;
+
+    rccs->BacktrackGlyphCount = GET_UShort();
+    
+    FORGET_Frame();
+
+    rccs->BacktrackCoverage = NULL;
+
+    backtrack_count = rccs->BacktrackGlyphCount;
+
+    if ( ALLOC_ARRAY( rccs->BacktrackCoverage, backtrack_count,
+                      TTO_Coverage ) )
+      goto Fail4;
+    
+    b = rccs->BacktrackCoverage;
+
+    for ( nb = 0; nb < backtrack_count; nb++ )
+    {
+      if ( ACCESS_Frame( 2L ) )
+        goto Fail3;
+
+      new_offset = GET_UShort() + base_offset;
+
+      FORGET_Frame();
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+           ( error = Load_Coverage( &b[nb], stream ) ) != TT_Err_Ok )
+        goto Fail3;
+      (void)FILE_Seek( cur_offset );
+    }
+
+
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail3;
+  
+    rccs->LookaheadGlyphCount = GET_UShort();
+    
+    FORGET_Frame();
+
+    rccs->LookaheadCoverage = NULL;
+
+    lookahead_count = rccs->LookaheadGlyphCount;
+
+    if ( ALLOC_ARRAY( rccs->LookaheadCoverage, lookahead_count,
+                      TTO_Coverage ) )
+      goto Fail3;
+
+    l = rccs->LookaheadCoverage;
+  
+    for ( nl = 0; nl < lookahead_count; nl++ )
+    {
+      if ( ACCESS_Frame( 2L ) )
+        goto Fail2;
+
+      new_offset = GET_UShort() + base_offset;
+
+      FORGET_Frame();
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+           ( error = Load_Coverage( &l[nl], stream ) ) != TT_Err_Ok )
+        goto Fail2;
+      (void)FILE_Seek( cur_offset );
+    }
+   
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+  
+    rccs->GlyphCount = GET_UShort();
+    
+    FORGET_Frame();
+
+    rccs->Substitute = NULL;
+
+    count = rccs->GlyphCount;
+
+    if ( ALLOC_ARRAY( rccs->Substitute, count,
+                      FT_UShort ) )
+      goto Fail2;
+
+    sub = rccs->Substitute;
+    
+    if ( ACCESS_Frame( count * 2L ) )
+      goto Fail1;
+    
+    for ( n = 0; n < count; n++ )
+      sub[n] = GET_UShort();
+            
+    FORGET_Frame();
+    
+    return TT_Err_Ok;
+
+  Fail1:
+    FREE( sub );
+    
+  Fail2:
+    for ( m = 0; m < nl; m++ )
+      Free_Coverage( &l[m], memory );
+
+    FREE( l );
+
+  Fail3:
+    for ( m = 0; m < nb; m++ )
+      Free_Coverage( &b[m], memory );
+
+    FREE( b );
+
+  Fail4:
+    Free_Coverage( &rccs->Coverage, memory );    
+    return error;
+  }
+
+
+  void  Free_ReverseChainContextSubst( TTO_ReverseChainContextSubst*  rccs,
+	 			       FT_Memory                      memory )
+  {  
+    FT_UShort      n, count;
+
+    TTO_Coverage*  c;
+
+    Free_Coverage( &rccs->Coverage, memory );
+    
+    if ( rccs->LookaheadCoverage )
+    {
+      count = rccs->LookaheadGlyphCount;
+      c     = rccs->LookaheadCoverage;
+
+      for ( n = 0; n < count; n++ )
+        Free_Coverage( &c[n], memory );
+
+      FREE( c );
+    }
+
+    if ( rccs->BacktrackCoverage )
+    {
+      count = rccs->BacktrackGlyphCount;
+      c     = rccs->BacktrackCoverage;
+
+      for ( n = 0; n < count; n++ )
+        Free_Coverage( &c[n], memory );
+
+      FREE( c );
+    }
+
+    FREE ( rccs->Substitute );
+  }
+  
+ 
+  static FT_Error  Lookup_ReverseChainContextSubst( TTO_GSUBHeader*                gsub,
+						    TTO_ReverseChainContextSubst*  rccs,
+						    OTL_Buffer                     buffer,
+						    FT_ULong                       string_index,
+						    FT_UShort                      flags )
+{
+    FT_UShort        index, input_index, i, j, property;
+    FT_UShort        bgc, lgc;
+    FT_Error         error;
+
+    TTO_Coverage*    bc;
+    TTO_Coverage*    lc;
+    TTO_GDEFHeader*  gdef;
+
+ 
+    gdef = gsub->gdef;
+
+    if ( CHECK_Property( gdef, IN_ITEM( string_index ), flags, &property ) )
+      return error;
+
+    bgc = rccs->BacktrackGlyphCount;
+    lgc = rccs->LookaheadGlyphCount;
+
+    /* check whether context is too long; it is a first guess only */
+    
+    if ( bgc > string_index || string_index + 1 + lgc > buffer->in_length )
+      return TTO_Err_Not_Covered;
+    
+    if ( bgc )
+    {
+      /* Since we don't know in advance the number of glyphs to inspect,
+         we search backwards for matches in the backtrack glyph array    */
+
+      bc       = rccs->BacktrackCoverage;
+
+      for ( i = 0, j = string_index - 1; i < bgc; i++, j-- )
+      {
+        while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+        {
+          if ( error && error != TTO_Err_Not_Covered )
+            return error;
+
+	  if ( j + 1 == bgc - i )
+	    return TTO_Err_Not_Covered;
+	  j--;
+        }
+
+        error = Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
+        if ( error )
+          return error;
+      }
+    }
+
+    j = string_index;
+   
+    error = Coverage_Index( &rccs->Coverage, IN_GLYPH( j ), &input_index );
+    if ( error )
+        return error;
+
+    /* we are starting for lookahead glyphs right after the last context
+       glyph                                                             */
+    
+    j += 1;
+
+    lc       = rccs->LookaheadCoverage;
+
+    for ( i = 0; i < lgc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+        if ( error && error != TTO_Err_Not_Covered )
+          return error;
 
+	if ( j + lgc - i == buffer->in_length )
+	  return TTO_Err_Not_Covered;
+	j++;
+      }
+
+      error = Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+      if ( error )
+        return error;
+    }
+
+    IN_GLYPH( string_index ) = rccs->Substitute[input_index];
+	
+    return error;
+  }
+
+
+ 
   /***********
    * GSUB API
    ***********/
@@ -4000,6 +4281,8 @@
                                           flags, context_length,
                                           nesting_level );
         break;
+      case GSUB_LOOKUP_REVERSE_CHAIN:
+	error = TTO_Err_Invalid_SubTable_Format;
       }
 
       /* Check whether we have a successful substitution or an error other
@@ -4052,6 +4335,47 @@
   }
 
 
+  static FT_Error  Apply_ReverseChainContextSubst( TTO_GSUBHeader*   gsub,
+						   FT_UShort         lookup_index,
+						   OTL_Buffer        buffer )
+  {
+    FT_UInt*     properties =  gsub->LookupList.Properties;
+    FT_Error     error, retError = TTO_Err_Not_Covered;
+    FT_ULong     subtable_Count, string_index;
+    FT_UShort    flags;
+    TTO_Lookup*  lo;      
+
+    if ( buffer->in_length == 0 )
+      return TTO_Err_Not_Covered;
+    
+    lo    = &gsub->LookupList.Lookup[lookup_index];
+    flags = lo->LookupFlag;      
+    
+    for ( subtable_Count = 0; subtable_Count < lo->SubTableCount; subtable_Count++ )
+    {
+      string_index  = buffer->in_length - 1;
+      do
+      {
+	if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+	  {
+	    error = Lookup_ReverseChainContextSubst( gsub, &lo->SubTable[subtable_Count].st.gsub.reverse,
+						     buffer, string_index, flags );
+	    if ( error )
+	      {
+		if ( error != TTO_Err_Not_Covered )
+		  return error;
+	      }
+	    else
+	      retError = error;
+	  }
+      }
+      while (string_index--);
+    }
+    
+    return retError;
+  }
+
+
   EXPORT_FUNC
   FT_Error  TT_GSUB_Add_Feature( TTO_GSUBHeader*  gsub,
                                  FT_UShort        feature_index,
@@ -4126,8 +4450,10 @@
 				  OTL_Buffer        buffer )
   {
     FT_Error          error, retError = TTO_Err_Not_Covered;
-    FT_UShort         i, j, feature_index;
+    FT_UShort         i, j, feature_index, lookup_index;
     TTO_Feature       feature;
+    TTO_Lookup*       lookup;
+    FT_Bool           need_swap;
 
     if ( !gsub ||
          !buffer || buffer->in_length == 0 || buffer->in_pos >= buffer->in_length )
@@ -4140,19 +4466,35 @@
 
       for ( j = 0; j < feature.LookupListCount; j++ )
       {
-        error = Do_String_Lookup( gsub, feature.LookupListIndex[j], buffer );
+	lookup_index = feature.LookupListIndex[j];
+	lookup = &gsub->LookupList.Lookup[lookup_index];
+	  
+	if ( lookup->LookupType == GSUB_LOOKUP_REVERSE_CHAIN )
+	{
+	  error = Apply_ReverseChainContextSubst( gsub, lookup_index, buffer);
+	  need_swap = FALSE; /* We do ReverseChainContextSubst in-place */
+	}
+	else
+	{
+	  error = Do_String_Lookup( gsub, lookup_index, buffer );
+	  need_swap = TRUE;
+	}
+	
 	if ( error )
 	{
 	  if ( error != TTO_Err_Not_Covered )
 	    goto End;
 	}
-	else
+        else 
 	  retError = error;
-        
-	error = otl_buffer_swap( buffer );
-	if ( error )
-	  goto End;
-      } 
+
+	if ( need_swap )
+	{
+	  error = otl_buffer_swap( buffer );
+	  if ( error )
+	    goto End;
+	}
+      }
     }
     
     error = retError;
Index: ftxgsub.h
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxgsub.h,v
retrieving revision 1.4
diff -u -p -r1.4 ftxgsub.h
--- ftxgsub.h	30 Jul 2004 21:23:04 -0000	1.4
+++ ftxgsub.h	15 Dec 2004 01:21:18 -0000
@@ -39,7 +39,7 @@ extern "C" {
 #define GSUB_LOOKUP_CONTEXT    5
 #define GSUB_LOOKUP_CHAIN      6
 #define GSUB_LOOKUP_EXTENSION  7
-
+#define GSUB_LOOKUP_REVERSE_CHAIN 8
 
 /* Use this if a feature applies to all glyphs */
 
@@ -467,6 +467,24 @@ extern "C" {
   typedef struct TTO_ChainContextSubst_  TTO_ChainContextSubst;
 
 
+  /* LookupType 8 */
+  struct TTO_ReverseChainContextSubst_
+  {
+    FT_UShort      SubstFormat;         /* always 1 */
+    TTO_Coverage   Coverage;	        /* coverage table for input glyphs */
+    FT_UShort      BacktrackGlyphCount; /* number of backtrack glyphs      */
+    TTO_Coverage*  BacktrackCoverage;   /* array of backtrack Coverage
+                                           tables                          */
+    FT_UShort      LookaheadGlyphCount; /* number of lookahead glyphs      */
+    TTO_Coverage*  LookaheadCoverage;   /* array of lookahead Coverage
+                                           tables                          */
+    FT_UShort      GlyphCount;          /* number of Glyph IDs             */
+    FT_UShort*     Substitute;          /* array of substitute Glyph ID    */
+  };
+
+  typedef struct TTO_ReverseChainContextSubst_  TTO_ReverseChainContextSubst;
+
+
   union  TTO_GSUB_SubTable_
   {
     TTO_SingleSubst        single;
@@ -475,6 +493,7 @@ extern "C" {
     TTO_LigatureSubst      ligature;
     TTO_ContextSubst       context;
     TTO_ChainContextSubst  chain;
+    TTO_ReverseChainContextSubst  reverse;
   };
 
   typedef union TTO_GSUB_SubTable_  TTO_GSUB_SubTable;
Index: ftxopen.c
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxopen.c,v
retrieving revision 1.13
diff -u -p -r1.13 ftxopen.c
--- ftxopen.c	30 Jul 2004 21:23:04 -0000	1.13
+++ ftxopen.c	15 Dec 2004 01:21:18 -0000
@@ -469,6 +469,9 @@
 
       case GSUB_LOOKUP_CHAIN:
         return Load_ChainContextSubst( &st->st.gsub.chain, stream );
+	
+      case GSUB_LOOKUP_REVERSE_CHAIN:
+        return Load_ReverseChainContextSubst( &st->st.gsub.reverse, stream );
 
       default:
         return TTO_Err_Invalid_GSUB_SubTable_Format;
@@ -534,6 +537,10 @@
 
       case GSUB_LOOKUP_CONTEXT:
         Free_ContextSubst( &st->st.gsub.context, memory );
+        break;
+
+      case GSUB_LOOKUP_REVERSE_CHAIN:
+        Free_ReverseChainContextSubst( &st->st.gsub.reverse, memory );
         break;
 
       case GSUB_LOOKUP_CHAIN:
Index: ftxopenf.h
===================================================================
RCS file: /cvs/gnome/pango/pango/opentype/ftxopenf.h,v
retrieving revision 1.3
diff -u -p -r1.3 ftxopenf.h
--- ftxopenf.h	26 Jul 2004 18:59:02 -0000	1.3
+++ ftxopenf.h	15 Dec 2004 01:21:18 -0000
@@ -74,6 +74,8 @@ extern "C" {
                                FT_Stream          input );
   FT_Error  Load_ChainContextSubst( TTO_ChainContextSubst*  ccs,
                                     FT_Stream               input );
+  FT_Error  Load_ReverseChainContextSubst( TTO_ReverseChainContextSubst*  rccs,
+					   FT_Stream                      input );
 
   void  Free_SingleSubst( TTO_SingleSubst*  ss,
 			  FT_Memory         memory );
@@ -87,7 +89,8 @@ extern "C" {
 			   FT_Memory         memory );
   void  Free_ChainContextSubst( TTO_ChainContextSubst*  ccs,
 				FT_Memory               memory );
-
+  void  Free_ReverseChainContextSubst( TTO_ReverseChainContextSubst*  rccs,
+				       FT_Memory                      memory );
 
   /* functions from ftxgpos.c */
 
