Rework opentype interfaces and other changes to make GPOS work for Arabic.
authorOwen Taylor <otaylor@redhat.com>
Sun, 29 Feb 2004 15:44:50 +0000 (15:44 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Sun, 29 Feb 2004 15:44:50 +0000 (15:44 +0000)
Sun Feb 29 09:25:13 2004  Owen Taylor  <otaylor@redhat.com>

        Rework opentype interfaces and other changes to make GPOS
        work for Arabic. (Most of #117282, #121060)

        * pango/opentype/otlbuffer.[ch]: OTL_Buffer that
        acts as a replacement for the separate GSUB and
        GPOS string structures and hides many of the internal
        details.

        * pango/opentype/ftxgsub.[ch] pango/opentype/ftxgpos.[ch]:
        Adapt to OTL_Buffer.

        * pango/opentype/ftxgpos.c: Redo handling of cursive
        chains so that it actually works.

        * pango/pango-ot.h pango/opentype/pango-ot-buffer.c:
        Pango wrapper around OTL_Buffer.

        * pango/pango-ot.h pango/pango-ot-ruleset.c pango/pango-ot-buffer.c:
        Split pango_ot_ruleset_shape() into pango_ot_ruleset_substitute(),
        pango_ot_ruleset_position(), make them act on
        PangoOTBuffer, add a separate pango_ot_buffer_output()
        which does the default positioning and writes to a
        PangoGlyphString.

        * modules/arabic/arabic-fc.c modules/indic/indic-fc.c
        modules/indic/mprefixups.[ch]: Adapt to new OpenType
        interfaces; add GPOS features for Arabic.

        * pango/opentype/pango-ot-info.c: Don't derive class information
        from Unicode properties for Arabic presentation forms,
        let the shaping process derive the properties.

14 files changed:
src/Makefile.am
src/ftxgdef.c
src/ftxgpos.c
src/ftxgpos.h
src/ftxgsub.c
src/ftxgsub.h
src/ftxopen.h
src/otlbuffer.c [new file with mode: 0644]
src/otlbuffer.h [new file with mode: 0644]
src/ottest.c
src/pango-ot-buffer.c [new file with mode: 0644]
src/pango-ot-info.c
src/pango-ot-private.h
src/pango-ot-ruleset.c

index 969b2db..4634b8c 100644 (file)
@@ -30,6 +30,9 @@ libpango_ot_la_SOURCES =      \
        ftxgpos.h               \
        ftxgsub.c               \
        ftxgsub.h               \
+       otlbuffer.c             \
+       otlbuffer.h             \
+       pango-ot-buffer.c       \
        pango-ot-info.c         \
        pango-ot-private.h      \
        pango-ot-ruleset.c
index 3614697..9077490 100644 (file)
 
     if ( glyphID < gcrr[index].Start )
     {
-      array_index = 0;
+      array_index = index;
       if ( index == 0 )
         glyph_index = glyphID;
       else
 
     if ( glyphID < gcrr[index].Start )
     {
-      array_index = 0;
+      array_index = index;
       if ( index == 0 )
         glyph_index = glyphID;
       else
index 8d9a7d4..8bbddcd 100644 (file)
@@ -42,8 +42,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 */
 
   static FT_Error  Do_Glyph_Lookup( GPOS_Instance*    gpi,
                                     FT_UShort         lookup_index,
-                                    TTO_GSUB_String*  in,
-                                    TTO_GPOS_Data*    out,
+                                   OTL_Buffer        buffer,
                                     FT_UShort         context_length,
                                     int               nesting_level );
 
 
-  /* the client application must replace this with something more
+#define IN_GLYPH( pos )        (buffer->in_string[(pos)].gindex)
+#define IN_CURGLYPH( pos )     (buffer->in_string[(pos) + buffer->in_pos].gindex)
+#define IN_PROPERTIES( pos )   (buffer->in_string[(pos)].properties)
+#define IN_LIGID( pos )        (buffer->in_string[(pos)].ligID)
+#define IN_COMPONENT( pos )    (buffer->in_string[(pos)].component)
+#define POSITION( pos )        (&buffer->positions[(pos)])
+
+/* the client application must replace this with something more
      meaningful if multiple master fonts are to be supported.     */
 
   static FT_Error  default_mmfunc( FT_Face      face,
   }
 
 
-#if 0
-#define GPOS_ID  Build_Extension_ID( 'G', 'P', 'O', 'S' )
-
-  /**********************
-   * Extension Functions
-   **********************/
-
-  static FT_Error  GPOS_Create( void*      ext,
-                                FT_Stream stream )
-  {
-    FT_Error  error;
-    FT_Memory memory = stream->memory;
-
-    TTO_GPOSHeader*  gpos = (TTO_GPOSHeader*)ext;
-    FT_Long          table;
-
-
-    /* by convention */
-
-    if ( !gpos )
-      return TT_Err_Ok;
-
-    /* a null offset indicates that there is no GPOS table */
-
-    gpos->offset = 0;
-
-    /* we store the start offset and the size of the subtable */
-
-    table = face->lookup_table ( face, TTAG_GPOS );
-    if ( table < 0 )
-      return TT_Err_Ok;             /* The table is optional */
-
-    if ( FILE_Seek( face->dirTables[table].Offset ) ||
-         ACCESS_Frame( 4L ) )
-      return error;
-
-    gpos->offset  = FILE_Pos() - 4L;    /* undo ACCESS_Frame() */
-    gpos->Version = GET_ULong();
-
-    FORGET_Frame();
-
-    /* a default mmfunc() handler which just returns an error */
-
-    gpos->mmfunc = default_mmfunc;
-
-    /* the default glyph function is TT_Load_Glyph() */
-
-    gpos->gfunc = FT_Load_Glyph;
-
-    gpos->loaded = FALSE;
-
-    return TT_Err_Ok;
-  }
-
-
-  static FT_Error  GPOS_Destroy( void*  ext,
-                                 PFace  face )
-  {
-    TTO_GPOSHeader*  gpos = (TTO_GPOSHeader*)ext;
-
-
-    /* by convention */
-
-    if ( !gpos )
-      return TT_Err_Ok;
-
-    if ( gpos->loaded )
-    {
-      Free_LookupList( &gpos->LookupList, GPOS );
-      Free_FeatureList( &gpos->FeatureList );
-      Free_ScriptList( &gpos->ScriptList );
-    }
-
-    return TT_Err_Ok;
-  }
-
-
-  EXPORT_FUNC
-  FT_Error  TT_Init_GPOS_Extension( TT_Engine  engine )
-  {
-    PEngine_Instance  _engine = HANDLE_Engine( engine );
-
-
-    if ( !_engine )
-      return TT_Err_Invalid_Engine;
-
-    return  TT_Register_Extension( _engine,
-                                   GPOS_ID,
-                                   sizeof ( TTO_GPOSHeader ),
-                                   GPOS_Create,
-                                   GPOS_Destroy );
-  }
-#endif
-
   EXPORT_FUNC
   FT_Error  TT_Load_GPOS_Table( FT_Face          face,
                                 TTO_GPOSHeader** retptr,
   static FT_Error  Get_ValueRecord( GPOS_Instance*    gpi,
                                     TTO_ValueRecord*  vr,
                                     FT_UShort         format,
-                                    TTO_GPOS_Data*    gd )
+                                    OTL_Position      gd )
   {
     FT_Pos           value;
     FT_Short         pixel_value;
 
   static FT_Error  Lookup_SinglePos( GPOS_Instance*    gpi,
                                      TTO_SinglePos*    sp,
-                                     TTO_GSUB_String*  in,
-                                     TTO_GPOS_Data*    out,
+                                    OTL_Buffer        buffer,
                                      FT_UShort         flags,
                                      FT_UShort         context_length )
   {
     if ( context_length != 0xFFFF && context_length < 1 )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gpos->gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gpos->gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &sp->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &sp->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     {
     case 1:
       error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
-                               sp->ValueFormat, &out[in->pos] );
+                               sp->ValueFormat, POSITION( buffer->in_pos ) );
       if ( error )
         return error;
       break;
       if ( index >= sp->spf.spf2.ValueCount )
         return TTO_Err_Invalid_GPOS_SubTable;
       error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
-                               sp->ValueFormat, &out[in->pos] );
+                               sp->ValueFormat, POSITION( buffer->in_pos ) );
       if ( error )
         return error;
       break;
       return TTO_Err_Invalid_GPOS_SubTable;
     }
 
-    (in->pos)++;
+    (buffer->in_pos)++;
 
     return TT_Err_Ok;
   }
 
   static FT_Error  Lookup_PairPos1( GPOS_Instance*       gpi,
                                     TTO_PairPosFormat1*  ppf1,
-                                    TTO_GSUB_String*     in,
-                                    TTO_GPOS_Data*       out,
+                                   OTL_Buffer           buffer,
                                     FT_UShort            first_pos,
                                     FT_UShort            index,
                                     FT_UShort            format1,
     if ( !pvr )
       return TTO_Err_Invalid_GPOS_SubTable;
 
-    glyph2 = in->string[in->pos];
+    glyph2 = IN_CURGLYPH( 0 );
 
     for ( numpvr = ppf1->PairSet[index].PairValueCount;
           numpvr;
       if ( glyph2 == pvr->SecondGlyph )
       {
         error = Get_ValueRecord( gpi, &pvr->Value1, format1,
-                                 &out[first_pos] );
+                                 POSITION( first_pos ) );
         if ( error )
           return error;
         return Get_ValueRecord( gpi, &pvr->Value2, format2,
-                                &out[in->pos] );
+                                POSITION( buffer->in_pos ) );
       }
     }
 
 
   static FT_Error  Lookup_PairPos2( GPOS_Instance*       gpi,
                                     TTO_PairPosFormat2*  ppf2,
-                                    TTO_GSUB_String*     in,
-                                    TTO_GPOS_Data*       out,
+                                   OTL_Buffer           buffer,
                                     FT_UShort            first_pos,
                                     FT_UShort            format1,
                                     FT_UShort            format2 )
     TTO_Class2Record*  c2r;
 
 
-    error = Get_Class( &ppf2->ClassDef1, in->string[first_pos],
+    error = Get_Class( &ppf2->ClassDef1, IN_GLYPH( first_pos ),
                        &cl1, NULL );
     if ( error && error != TTO_Err_Not_Covered )
       return error;
-    error = Get_Class( &ppf2->ClassDef2, in->string[in->pos],
+    error = Get_Class( &ppf2->ClassDef2, IN_CURGLYPH( 0 ),
                        &cl2, NULL );
     if ( error && error != TTO_Err_Not_Covered )
       return error;
       return TTO_Err_Invalid_GPOS_SubTable;
     c2r = &c1r->Class2Record[cl2];
 
-    error = Get_ValueRecord( gpi, &c2r->Value1, format1, &out[first_pos] );
+    error = Get_ValueRecord( gpi, &c2r->Value1, format1, POSITION( first_pos ) );
     if ( error )
       return error;
-    return Get_ValueRecord( gpi, &c2r->Value2, format2, &out[in->pos] );
+    return Get_ValueRecord( gpi, &c2r->Value2, format2, POSITION( buffer->in_pos ) );
   }
 
 
   static FT_Error  Lookup_PairPos( GPOS_Instance*    gpi,
                                    TTO_PairPos*      pp,
-                                   TTO_GSUB_String*  in,
-                                   TTO_GPOS_Data*    out,
+                                  OTL_Buffer        buffer,
                                    FT_UShort         flags,
                                    FT_UShort         context_length )
   {
     TTO_GPOSHeader*  gpos = gpi->gpos;
 
 
-    if ( in->pos >= in->length - 1 )
+    if ( buffer->in_pos >= buffer->in_length - 1 )
       return TTO_Err_Not_Covered;           /* Not enough glyphs in stream */
 
     if ( context_length != 0xFFFF && context_length < 2 )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gpos->gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gpos->gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &pp->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &pp->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     /* second glyph */
 
-    first_pos = in->pos;
-    (in->pos)++;
+    first_pos = buffer->in_pos;
+    (buffer->in_pos)++;
 
-    while ( CHECK_Property( gpos->gdef, in->string[in->pos],
+    while ( CHECK_Property( gpos->gdef, IN_CURGLYPH( 0 ),
                             flags, &property ) )
     {
       if ( error && error != TTO_Err_Not_Covered )
         return error;
 
-      if ( in->pos < in->length )
-        (in->pos)++;
+      if ( buffer->in_pos < buffer->in_length )
+        (buffer->in_pos)++;
       else
         break;
     }
     switch ( pp->PosFormat )
     {
     case 1:
-      error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, in, out,
+      error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer,
                                first_pos, index,
                                pp->ValueFormat1, pp->ValueFormat2 );
       break;
 
     case 2:
-      error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, in, out, first_pos,
+      error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos,
                                pp->ValueFormat1, pp->ValueFormat2 );
       break;
 
     /* adjusting the `next' glyph */
 
     if ( pp->ValueFormat2 )
-      (in->pos)++;
+      (buffer->in_pos)++;
 
     return error;
   }
 
   static FT_Error  Lookup_CursivePos( GPOS_Instance*    gpi,
                                       TTO_CursivePos*   cp,
-                                      TTO_GSUB_String*  in,
-                                      TTO_GPOS_Data*    out,
+                                     OTL_Buffer        buffer,
                                       FT_UShort         flags,
                                       FT_UShort         context_length )
   {
     /* Glyphs not having the right GDEF properties will be ignored, i.e.,
        gpi->last won't be reset (contrary to user defined properties). */
 
-    if ( CHECK_Property( gpos->gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gpos->gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     /* We don't handle mark glyphs here.  According to Andrei, this isn't
       return TTO_Err_Not_Covered;
     }
 
-    error = Coverage_Index( &cp->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &cp->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
     {
       gpi->last = 0xFFFF;
     /* Get_Anchor() returns TTO_Err_Not_Covered if there is no anchor
        table.                                                         */
 
-    error = Get_Anchor( gpi, &eer->EntryAnchor, in->string[in->pos],
+    error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH( 0 ),
                         &entry_x, &entry_y );
     if ( error == TTO_Err_Not_Covered )
       goto end;
 
     if ( gpi->r2l )
     {
-      out[in->pos].x_advance   = entry_x - gpi->anchor_x;
-      out[in->pos].new_advance = TRUE;
+      POSITION( buffer->in_pos )->x_advance   = entry_x - gpi->anchor_x;
+      POSITION( buffer->in_pos )->new_advance = TRUE;
     }
     else
     {
-      out[gpi->last].x_advance   = gpi->anchor_x - entry_x;
-      out[gpi->last].new_advance = TRUE;
+      POSITION( gpi->last )->x_advance   = gpi->anchor_x - entry_x;
+      POSITION( 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 )
+    {
+      POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos;
+      POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y;
+    }
+    else
+    {
+      POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last;
+      POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y;
+    }
 
   end:
-    error = Get_Anchor( gpi, &eer->ExitAnchor, in->string[in->pos],
+    error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH( 0 ),
                         &exit_x, &exit_y );
     if ( error == TTO_Err_Not_Covered )
       gpi->last = 0xFFFF;
     else
     {
-      if ( gpi->first == 0xFFFF )
-        gpi->first  = in->pos;
-      gpi->last     = in->pos;
+      gpi->last     = buffer->in_pos;
       gpi->anchor_x = exit_x;
       gpi->anchor_y = exit_y;
     }
     if ( error )
       return error;
 
-    (in->pos)++;
+    (buffer->in_pos)++;
 
     return TT_Err_Ok;
   }
 
   static FT_Error  Lookup_MarkBasePos( GPOS_Instance*    gpi,
                                        TTO_MarkBasePos*  mbp,
-                                       TTO_GSUB_String*  in,
-                                       TTO_GPOS_Data*    out,
+                                      OTL_Buffer        buffer,
                                        FT_UShort         flags,
                                        FT_UShort         context_length )
   {
     TTO_Anchor*      mark_anchor;
     TTO_Anchor*      base_anchor;
 
-    TTO_GPOS_Data*   o;
+    OTL_Position     o;
 
 
     if ( context_length != 0xFFFF && context_length < 1 )
     if ( flags & IGNORE_BASE_GLYPHS )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gpos->gdef, in->string[in->pos],
+    if ( CHECK_Property( gpos->gdef, IN_CURGLYPH( 0 ),
                          flags, &property ) )
       return error;
 
-    error = Coverage_Index( &mbp->MarkCoverage, in->string[in->pos],
+    error = Coverage_Index( &mbp->MarkCoverage, IN_CURGLYPH( 0 ),
                             &mark_index );
     if ( error )
       return error;
     /* now we search backwards for a non-mark glyph */
 
     i = 1;
-    j = in->pos - 1;
+    j = buffer->in_pos - 1;
 
-    while ( i <= in->pos )
+    while ( i <= buffer->in_pos )
     {
-      error = TT_GDEF_Get_Glyph_Property( gpos->gdef, in->string[j],
+      error = TT_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
                                           &property );
       if ( error )
         return error;
       return TTO_Err_Not_Covered;
 #endif
 
-    if ( i > in->pos )
+    if ( i > buffer->in_pos )
       return TTO_Err_Not_Covered;
 
-    error = Coverage_Index( &mbp->BaseCoverage, in->string[j],
+    error = Coverage_Index( &mbp->BaseCoverage, IN_GLYPH( j ),
                             &base_index );
     if ( error )
       return error;
     br          = &ba->BaseRecord[base_index];
     base_anchor = &br->BaseAnchor[class];
 
-    error = Get_Anchor( gpi, mark_anchor, in->string[in->pos],
+    error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH( 0 ),
                         &x_mark_value, &y_mark_value );
     if ( error )
       return error;
 
-    error = Get_Anchor( gpi, base_anchor, in->string[j],
+    error = Get_Anchor( gpi, base_anchor, IN_GLYPH( j ),
                         &x_base_value, &y_base_value );
     if ( error )
       return error;
 
     /* anchor points are not cumulative */
 
-    o = &out[in->pos];
+    o = POSITION( buffer->in_pos );
 
     o->x_pos     = x_base_value - x_mark_value;
     o->y_pos     = y_base_value - y_mark_value;
     o->y_advance = 0;
     o->back      = i;
 
-    (in->pos)++;
+    (buffer->in_pos)++;
 
     return TT_Err_Ok;
   }
 
   static FT_Error  Lookup_MarkLigPos( GPOS_Instance*    gpi,
                                       TTO_MarkLigPos*   mlp,
-                                      TTO_GSUB_String*  in,
-                                      TTO_GPOS_Data*    out,
+                                     OTL_Buffer        buffer,
                                       FT_UShort         flags,
                                       FT_UShort         context_length )
   {
     TTO_Anchor*           mark_anchor;
     TTO_Anchor*           lig_anchor;
 
-    TTO_GPOS_Data*  o;
+    OTL_Position    o;
 
 
     if ( context_length != 0xFFFF && context_length < 1 )
     if ( flags & IGNORE_LIGATURES )
       return TTO_Err_Not_Covered;
 
-    mark_glyph = in->string[in->pos];
+    mark_glyph = IN_CURGLYPH( 0 );
 
     if ( CHECK_Property( gpos->gdef, mark_glyph, flags, &property ) )
       return error;
     /* now we search backwards for a non-mark glyph */
 
     i = 1;
-    j = in->pos - 1;
+    j = buffer->in_pos - 1;
 
-    while ( i <= in->pos )
+    while ( i <= buffer->in_pos )
     {
-      error = TT_GDEF_Get_Glyph_Property( gpos->gdef, in->string[j],
+      error = TT_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
                                           &property );
       if ( error )
         return error;
       return TTO_Err_Not_Covered;
 #endif
 
-    if ( i > in->pos )
+    if ( i > buffer->in_pos )
       return TTO_Err_Not_Covered;
 
-    error = Coverage_Index( &mlp->LigatureCoverage, in->string[j],
+    error = Coverage_Index( &mlp->LigatureCoverage, IN_GLYPH( j ),
                             &lig_index );
     if ( error )
       return error;
        can directly use the component index.  If not, we attach the mark
        glyph to the last component of the ligature.                        */
 
-    if ( in->ligIDs && in->components &&
-         in->ligIDs[j] == in->ligIDs[in->pos] )
+    if ( IN_LIGID( j ) == IN_LIGID( buffer->in_pos) )
     {
-      comp_index = in->components[in->pos];
+      comp_index = IN_COMPONENT( buffer->in_pos );
       if ( comp_index >= lat->ComponentCount )
         return TTO_Err_Not_Covered;
     }
     cr         = &lat->ComponentRecord[comp_index];
     lig_anchor = &cr->LigatureAnchor[class];
 
-    error = Get_Anchor( gpi, mark_anchor, in->string[in->pos],
+    error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH( 0 ),
                         &x_mark_value, &y_mark_value );
     if ( error )
       return error;
-    error = Get_Anchor( gpi, lig_anchor, in->string[j],
+    error = Get_Anchor( gpi, lig_anchor, IN_GLYPH( j ),
                         &x_lig_value, &y_lig_value );
     if ( error )
       return error;
 
     /* anchor points are not cumulative */
 
-    o = &out[in->pos];
+    o = POSITION( buffer->in_pos );
 
     o->x_pos     = x_lig_value - x_mark_value;
     o->y_pos     = y_lig_value - y_mark_value;
     o->y_advance = 0;
     o->back      = i;
 
-    (in->pos)++;
+    (buffer->in_pos)++;
 
     return TT_Err_Ok;
   }
 
   static FT_Error  Lookup_MarkMarkPos( GPOS_Instance*    gpi,
                                        TTO_MarkMarkPos*  mmp,
-                                       TTO_GSUB_String*  in,
-                                       TTO_GPOS_Data*    out,
+                                      OTL_Buffer        buffer,
                                        FT_UShort         flags,
                                        FT_UShort         context_length )
   {
     TTO_Anchor*       mark1_anchor;
     TTO_Anchor*       mark2_anchor;
 
-    TTO_GPOS_Data*  o;
+    OTL_Position    o;
 
 
     if ( context_length != 0xFFFF && context_length < 1 )
     if ( flags & IGNORE_MARKS )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gpos->gdef, in->string[in->pos],
+    if ( CHECK_Property( gpos->gdef, IN_CURGLYPH( 0 ),
                          flags, &property ) )
       return error;
 
-    error = Coverage_Index( &mmp->Mark1Coverage, in->string[in->pos],
+    error = Coverage_Index( &mmp->Mark1Coverage, IN_CURGLYPH( 0 ),
                             &mark1_index );
     if ( error )
       return error;
     /* now we check the preceding glyph whether it is a suitable
        mark glyph                                                */
 
-    if ( in->pos == 0 )
+    if ( buffer->in_pos == 0 )
       return TTO_Err_Not_Covered;
 
-    j = in->pos - 1;
-    error = TT_GDEF_Get_Glyph_Property( gpos->gdef, in->string[j],
+    j = buffer->in_pos - 1;
+    error = TT_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
                                         &property );
     if ( error )
       return error;
         return TTO_Err_Not_Covered;
     }
 
-    error = Coverage_Index( &mmp->Mark2Coverage, in->string[j],
+    error = Coverage_Index( &mmp->Mark2Coverage, IN_GLYPH( j ),
                             &mark2_index );
     if ( error )
       return error;
     m2r          = &ma2->Mark2Record[mark2_index];
     mark2_anchor = &m2r->Mark2Anchor[class];
 
-    error = Get_Anchor( gpi, mark1_anchor, in->string[in->pos],
+    error = Get_Anchor( gpi, mark1_anchor, IN_CURGLYPH( 0 ),
                         &x_mark1_value, &y_mark1_value );
     if ( error )
       return error;
-    error = Get_Anchor( gpi, mark2_anchor, in->string[j],
+    error = Get_Anchor( gpi, mark2_anchor, IN_GLYPH( j ),
                         &x_mark2_value, &y_mark2_value );
     if ( error )
       return error;
 
     /* anchor points are not cumulative */
 
-    o = &out[in->pos];
+    o = POSITION( buffer->in_pos );
 
     o->x_pos     = x_mark2_value - x_mark1_value;
     o->y_pos     = y_mark2_value - y_mark1_value;
     o->y_advance = 0;
     o->back      = 1;
 
-    (in->pos)++;
+    (buffer->in_pos)++;
 
     return TT_Err_Ok;
   }
                                   FT_UShort             GlyphCount,
                                   FT_UShort             PosCount,
                                   TTO_PosLookupRecord*  pos,
-                                  TTO_GSUB_String*      in,
-                                  TTO_GPOS_Data*        out,
+                                 OTL_Buffer            buffer,
                                   int                   nesting_level )
   {
     FT_Error  error;
     {
       if ( PosCount && i == pos->SequenceIndex )
       {
-        old_pos = in->pos;
+        old_pos = buffer->in_pos;
 
         /* Do a positioning */
 
-        error = Do_Glyph_Lookup( gpi, pos->LookupListIndex, in, out,
+        error = Do_Glyph_Lookup( gpi, pos->LookupListIndex, buffer,
                                  GlyphCount, nesting_level );
 
         if ( error )
 
         pos++;
         PosCount--;
-        i += in->pos - old_pos;
+        i += buffer->in_pos - old_pos;
       }
       else
       {
         i++;
-        (in->pos)++;
+        (buffer->in_pos)++;
       }
     }
 
 
   static FT_Error  Lookup_ContextPos1( GPOS_Instance*          gpi,
                                        TTO_ContextPosFormat1*  cpf1,
-                                       TTO_GSUB_String*        in,
-                                       TTO_GPOS_Data*          out,
+                                      OTL_Buffer              buffer,
                                        FT_UShort               flags,
                                        FT_UShort               context_length,
                                        int                     nesting_level )
     FT_UShort        index, property;
     FT_UShort        i, j, k, numpr;
     FT_Error         error;
-    FT_UShort*       s_in;
     TTO_GPOSHeader*  gpos = gpi->gpos;
 
     TTO_PosRule*     pr;
 
     gdef = gpos->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &cpf1->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &cpf1->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
       if ( context_length != 0xFFFF && context_length < pr[k].GlyphCount )
         continue;
 
-      if ( in->pos + pr[k].GlyphCount > in->length )
+      if ( buffer->in_pos + pr[k].GlyphCount > buffer->in_length )
         continue;                           /* context is too long */
 
-      s_in = &in->string[in->pos];
-
       for ( i = 1, j = 1; i < pr[k].GlyphCount; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( in->pos + j < in->length )
+          if ( buffer->in_pos + j < buffer->in_length )
             j++;
           else
             break;
         }
 
-        if ( s_in[j] != pr[k].Input[i - 1] )
+        if ( IN_CURGLYPH( j ) != pr[k].Input[i - 1] )
           break;
       }
 
       if ( i == pr[k].GlyphCount )
         return Do_ContextPos( gpi, pr[k].GlyphCount,
                               pr[k].PosCount, pr[k].PosLookupRecord,
-                              in, out,
+                              buffer,
                               nesting_level );
     }
 
 
   static FT_Error  Lookup_ContextPos2( GPOS_Instance*          gpi,
                                        TTO_ContextPosFormat2*  cpf2,
-                                       TTO_GSUB_String*        in,
-                                       TTO_GPOS_Data*          out,
+                                      OTL_Buffer              buffer,
                                        FT_UShort               flags,
                                        FT_UShort               context_length,
                                        int                     nesting_level )
     FT_UShort          i, j, k, known_classes;
 
     FT_UShort*         classes;
-    FT_UShort*         s_in;
     FT_UShort*         cl;
     TTO_GPOSHeader*    gpos = gpi->gpos;
 
 
     gdef = gpos->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     /* Note: The coverage table in format 2 doesn't give an index into
              anything.  It just lets us know whether or not we need to
              do any lookup at all.                                     */
 
-    error = Coverage_Index( &cpf2->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &cpf2->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, FT_UShort ) )
       return error;
 
-    error = Get_Class( &cpf2->ClassDef, in->string[in->pos],
+    error = Get_Class( &cpf2->ClassDef, IN_CURGLYPH( 0 ),
                        &classes[0], NULL );
     if ( error && error != TTO_Err_Not_Covered )
       goto End;
       if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
         continue;
 
-      if ( in->pos + pr->GlyphCount > in->length )
+      if ( buffer->in_pos + pr->GlyphCount > buffer->in_length )
         continue;                           /* context is too long */
 
-      s_in = &in->string[in->pos];
       cl   = pr->Class;
 
       /* Start at 1 because [0] is implied */
 
       for ( i = 1, j = 1; i < pr->GlyphCount; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             goto End;
 
-          if ( in->pos + j < in->length )
+          if ( buffer->in_pos + j < buffer->in_length )
             j++;
           else
             break;
         {
           /* Keeps us from having to do this for each rule */
 
-          error = Get_Class( &cpf2->ClassDef, s_in[j], &classes[i], NULL );
+          error = Get_Class( &cpf2->ClassDef, IN_CURGLYPH( j ), &classes[i], NULL );
           if ( error && error != TTO_Err_Not_Covered )
             goto End;
           known_classes = i;
       {
         error = Do_ContextPos( gpi, pr->GlyphCount,
                                pr->PosCount, pr->PosLookupRecord,
-                               in, out,
+                               buffer,
                                nesting_level );
         goto End;
       }
 
   static FT_Error  Lookup_ContextPos3( GPOS_Instance*          gpi,
                                        TTO_ContextPosFormat3*  cpf3,
-                                       TTO_GSUB_String*        in,
-                                       TTO_GPOS_Data*          out,
+                                      OTL_Buffer              buffer,
                                        FT_UShort               flags,
                                        FT_UShort               context_length,
                                        int                     nesting_level )
   {
     FT_Error         error;
     FT_UShort        index, i, j, property;
-    FT_UShort*       s_in;
     TTO_GPOSHeader*  gpos = gpi->gpos;
 
     TTO_Coverage*    c;
 
     gdef = gpos->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
       return TTO_Err_Not_Covered;
 
-    if ( in->pos + cpf3->GlyphCount > in->length )
+    if ( buffer->in_pos + cpf3->GlyphCount > buffer->in_length )
       return TTO_Err_Not_Covered;         /* context is too long */
 
-    s_in = &in->string[in->pos];
     c    = cpf3->Coverage;
 
     for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
     {
-      while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+      while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
       {
         if ( error && error != TTO_Err_Not_Covered )
           return error;
 
-        if ( in->pos + j < in->length )
+        if ( buffer->in_pos + j < buffer->in_length )
           j++;
         else
           return TTO_Err_Not_Covered;
       }
 
-      error = Coverage_Index( &c[i], s_in[j], &index );
+      error = Coverage_Index( &c[i], IN_CURGLYPH( j ), &index );
       if ( error )
         return error;
     }
 
     return Do_ContextPos( gpi, cpf3->GlyphCount,
                           cpf3->PosCount, cpf3->PosLookupRecord,
-                          in, out,
+                          buffer,
                           nesting_level );
   }
 
 
   static FT_Error  Lookup_ContextPos( GPOS_Instance*    gpi,
                                       TTO_ContextPos*   cp,
-                                      TTO_GSUB_String*  in,
-                                      TTO_GPOS_Data*    out,
+                                     OTL_Buffer        buffer,
                                       FT_UShort         flags,
                                       FT_UShort         context_length,
                                       int               nesting_level )
     switch ( cp->PosFormat )
     {
     case 1:
-      return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, in, out,
+      return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, buffer,
                                  flags, context_length, nesting_level );
 
     case 2:
-      return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, in, out,
+      return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, buffer,
                                  flags, context_length, nesting_level );
 
     case 3:
-      return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, in, out,
+      return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, buffer,
                                  flags, context_length, nesting_level );
 
     default:
   static FT_Error  Lookup_ChainContextPos1(
                      GPOS_Instance*               gpi,
                      TTO_ChainContextPosFormat1*  ccpf1,
-                     TTO_GSUB_String*             in,
-                     TTO_GPOS_Data*               out,
+                    OTL_Buffer                   buffer,
                      FT_UShort                    flags,
                      FT_UShort                    context_length,
                      int                          nesting_level )
     FT_UShort          i, j, k, num_cpr, curr_pos;
     FT_UShort          bgc, igc, lgc;
     FT_Error           error;
-    FT_UShort*         s_in;
     TTO_GPOSHeader*    gpos = gpi->gpos;
 
     TTO_ChainPosRule*  cpr;
 
     gdef = gpos->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &ccpf1->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ccpf1->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
 
       /* check whether context is too long; it is a first guess only */
 
-      if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+      if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
         continue;
 
       if ( bgc )
            we search backwards for matches in the backtrack glyph array    */
 
         curr_pos = 0;
-        s_in     = &in->string[curr_pos];
 
-        for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+        for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
         {
-          while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+          while ( CHECK_Property( gdef, IN_GLYPH( j ), flags, &property ) )
           {
             if ( error && error != TTO_Err_Not_Covered )
               return error;
                Backtrack offsets -  3  2  1  0
                Lookahead offsets -                    0  1  2  3           */
 
-          if ( s_in[j] != curr_cpr.Backtrack[i] )
+          if ( IN_GLYPH( j ) != curr_cpr.Backtrack[i] )
             break;
         }
 
           continue;
       }
 
-      curr_pos = in->pos;
-      s_in     = &in->string[curr_pos];
+      curr_pos = buffer->in_pos;
 
       /* Start at 1 because [0] is implied */
 
       for ( i = 1, j = 1; i < igc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
         }
 
-        if ( s_in[j] != curr_cpr.Input[i - 1] )
+        if ( IN_GLYPH( curr_pos + j ) != curr_cpr.Input[i - 1] )
           break;
       }
 
          last context glyph                                            */
 
       curr_pos += j;
-      s_in     = &in->string[curr_pos];
 
       for ( i = 0, j = 0; i < lgc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
         }
 
-        if ( s_in[j] != curr_cpr.Lookahead[i] )
+        if ( IN_GLYPH( curr_pos + j ) != curr_cpr.Lookahead[i] )
           break;
       }
 
         return Do_ContextPos( gpi, igc,
                               curr_cpr.PosCount,
                               curr_cpr.PosLookupRecord,
-                              in, out,
+                              buffer,
                               nesting_level );
     }
 
   static FT_Error  Lookup_ChainContextPos2(
                      GPOS_Instance*               gpi,
                      TTO_ChainContextPosFormat2*  ccpf2,
-                     TTO_GSUB_String*             in,
-                     TTO_GPOS_Data*               out,
+                    OTL_Buffer                   buffer,
                      FT_UShort                    flags,
                      FT_UShort                    context_length,
                      int                          nesting_level )
     FT_UShort*             input_classes;
     FT_UShort*             lookahead_classes;
 
-    FT_UShort*             s_in;
-
     FT_UShort*             bc;
     FT_UShort*             ic;
     FT_UShort*             lc;
 
     gdef = gpos->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     /* Note: The coverage table in format 2 doesn't give an index into
              anything.  It just lets us know whether or not we need to
              do any lookup at all.                                     */
 
-    error = Coverage_Index( &ccpf2->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ccpf2->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
       goto End2;
     known_lookahead_classes = 0;
 
-    error = Get_Class( &ccpf2->InputClassDef, in->string[in->pos],
+    error = Get_Class( &ccpf2->InputClassDef, IN_CURGLYPH( 0 ),
                        &input_classes[0], NULL );
     if ( error && error != TTO_Err_Not_Covered )
       goto End1;
 
       /* check whether context is too long; it is a first guess only */
 
-      if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+      if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
         continue;
 
       if ( bgc )
            Note that `known_backtrack_classes' starts at index 0.         */
 
         curr_pos = 0;
-        s_in     = &in->string[curr_pos];
         bc       = cpcr.Backtrack;
 
-        for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+        for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
         {
-          while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+          while ( CHECK_Property( gdef, IN_GLYPH( j ), flags, &property ) )
           {
             if ( error && error != TTO_Err_Not_Covered )
               goto End1;
           {
             /* Keeps us from having to do this for each rule */
 
-            error = Get_Class( &ccpf2->BacktrackClassDef, s_in[j],
+            error = Get_Class( &ccpf2->BacktrackClassDef, IN_GLYPH( j ),
                                &backtrack_classes[i], NULL );
             if ( error && error != TTO_Err_Not_Covered )
               goto End1;
           continue;
       }
 
-      curr_pos = in->pos;
-      s_in     = &in->string[curr_pos];
+      curr_pos = buffer->in_pos;
       ic       = cpcr.Input;
 
       /* Start at 1 because [0] is implied */
 
       for ( i = 1, j = 1; i < igc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
 
         if ( i >= known_input_classes )
         {
-          error = Get_Class( &ccpf2->InputClassDef, s_in[j],
+          error = Get_Class( &ccpf2->InputClassDef, IN_GLYPH( curr_pos + j ),
                              &input_classes[i], NULL );
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
          last context glyph                                            */
 
       curr_pos += j;
-      s_in     = &in->string[curr_pos];
       lc       = cpcr.Lookahead;
 
       for ( i = 0, j = 0; i < lgc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
 
         if ( i >= known_lookahead_classes )
         {
-          error = Get_Class( &ccpf2->LookaheadClassDef, s_in[j],
+          error = Get_Class( &ccpf2->LookaheadClassDef, IN_GLYPH( curr_pos + j ),
                              &lookahead_classes[i], NULL );
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
         error = Do_ContextPos( gpi, igc,
                                cpcr.PosCount,
                                cpcr.PosLookupRecord,
-                               in, out,
+                               buffer,
                                nesting_level );
         goto End1;
       }
   static FT_Error  Lookup_ChainContextPos3(
                      GPOS_Instance*               gpi,
                      TTO_ChainContextPosFormat3*  ccpf3,
-                     TTO_GSUB_String*             in,
-                     TTO_GPOS_Data*               out,
+                    OTL_Buffer                   buffer,
                      FT_UShort                    flags,
                      FT_UShort                    context_length,
                      int                          nesting_level )
     FT_UShort        index, i, j, curr_pos, property;
     FT_UShort        bgc, igc, lgc;
     FT_Error         error;
-    FT_UShort*       s_in;
     TTO_GPOSHeader*  gpos = gpi->gpos;
 
     TTO_Coverage*    bc;
 
     gdef = gpos->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     bgc = ccpf3->BacktrackGlyphCount;
 
     /* check whether context is too long; it is a first guess only */
 
-    if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+    if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
       return TTO_Err_Not_Covered;
 
     if ( bgc )
          we search backwards for matches in the backtrack glyph array    */
 
       curr_pos = 0;
-      s_in     = &in->string[curr_pos];
       bc       = ccpf3->BacktrackCoverage;
 
-      for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+      for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
             return TTO_Err_Not_Covered;
         }
 
-        error = Coverage_Index( &bc[i], s_in[j], &index );
+        error = Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
         if ( error )
           return error;
       }
     }
 
-    curr_pos = in->pos;
-    s_in     = &in->string[curr_pos];
+    curr_pos = buffer->in_pos;
     ic       = ccpf3->InputCoverage;
 
     for ( i = 0, j = 0; i < igc; i++, j++ )
     {
       /* We already called CHECK_Property for s_in[0] */
-      while ( j > 0 && CHECK_Property( gdef, s_in[j], flags, &property ) )
+      while ( j > 0 && CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
       {
         if ( error && error != TTO_Err_Not_Covered )
           return error;
 
-        if ( curr_pos + j < in->length )
+        if ( curr_pos + j < buffer->in_length )
           j++;
         else
           return TTO_Err_Not_Covered;
       }
 
-      error = Coverage_Index( &ic[i], s_in[j], &index );
+      error = Coverage_Index( &ic[i], IN_GLYPH( curr_pos + j ), &index );
       if ( error )
         return error;
     }
        last context glyph                                            */
 
     curr_pos += j;
-    s_in     = &in->string[curr_pos];
     lc       = ccpf3->LookaheadCoverage;
 
     for ( i = 0, j = 0; i < lgc; i++, j++ )
     {
-      while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+      while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
       {
         if ( error && error != TTO_Err_Not_Covered )
           return error;
 
-        if ( curr_pos + j < in->length )
+        if ( curr_pos + j < buffer->in_length )
           j++;
         else
           return TTO_Err_Not_Covered;
       }
 
-      error = Coverage_Index( &lc[i], s_in[j], &index );
+      error = Coverage_Index( &lc[i], IN_GLYPH( curr_pos + j ), &index );
       if ( error )
         return error;
     }
     return Do_ContextPos( gpi, igc,
                           ccpf3->PosCount,
                           ccpf3->PosLookupRecord,
-                          in, out,
+                          buffer,
                           nesting_level );
   }
 
   static FT_Error  Lookup_ChainContextPos(
                      GPOS_Instance*        gpi,
                      TTO_ChainContextPos*  ccp,
-                     TTO_GSUB_String*      in,
-                     TTO_GPOS_Data*        out,
+                    OTL_Buffer            buffer,
                      FT_UShort             flags,
                      FT_UShort             context_length,
                      int                   nesting_level )
     switch ( ccp->PosFormat )
     {
     case 1:
-      return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, in, out,
+      return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, buffer,
                                       flags, context_length,
                                       nesting_level );
 
     case 2:
-      return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, in, out,
+      return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, buffer,
                                       flags, context_length,
                                       nesting_level );
 
     case 3:
-      return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, in, out,
+      return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, buffer,
                                       flags, context_length,
                                       nesting_level );
 
 
   static FT_Error  Do_Glyph_Lookup( GPOS_Instance*    gpi,
                                     FT_UShort         lookup_index,
-                                    TTO_GSUB_String*  in,
-                                    TTO_GPOS_Data*    out,
+                                   OTL_Buffer        buffer,
                                     FT_UShort         context_length,
                                     int               nesting_level )
   {
       case GPOS_LOOKUP_SINGLE:
         error = Lookup_SinglePos( gpi,
                                   &lo->SubTable[i].st.gpos.single,
-                                  in, out,
+                                  buffer,
                                   flags, context_length );
         break;
 
       case GPOS_LOOKUP_PAIR:
         error = Lookup_PairPos( gpi,
                                 &lo->SubTable[i].st.gpos.pair,
-                                in, out,
+                                buffer,
                                 flags, context_length );
         break;
 
       case GPOS_LOOKUP_CURSIVE:
         error = Lookup_CursivePos( gpi,
                                    &lo->SubTable[i].st.gpos.cursive,
-                                   in, out,
+                                   buffer,
                                    flags, context_length );
         break;
 
       case GPOS_LOOKUP_MARKBASE:
         error = Lookup_MarkBasePos( gpi,
                                     &lo->SubTable[i].st.gpos.markbase,
-                                    in, out,
+                                    buffer,
                                     flags, context_length );
         break;
 
       case GPOS_LOOKUP_MARKLIG:
         error = Lookup_MarkLigPos( gpi,
                                    &lo->SubTable[i].st.gpos.marklig,
-                                   in, out,
+                                   buffer,
                                    flags, context_length );
         break;
 
       case GPOS_LOOKUP_MARKMARK:
         error = Lookup_MarkMarkPos( gpi,
                                     &lo->SubTable[i].st.gpos.markmark,
-                                    in, out,
+                                    buffer,
                                     flags, context_length );
         break;
 
       case GPOS_LOOKUP_CONTEXT:
         error = Lookup_ContextPos( gpi,
                                    &lo->SubTable[i].st.gpos.context,
-                                   in, out,
+                                   buffer,
                                    flags, context_length,
                                    nesting_level );
         break;
       case GPOS_LOOKUP_CHAIN:
         error = Lookup_ChainContextPos( gpi,
                                         &lo->SubTable[i].st.gpos.chain,
-                                        in, out,
+                                        buffer,
                                         flags, context_length,
                                         nesting_level );
         break;
 
   static FT_Error  Do_String_Lookup( GPOS_Instance*    gpi,
                                      FT_UShort         lookup_index,
-                                     TTO_GSUB_String*  in,
-                                     TTO_GPOS_Data*    out )
+                                    OTL_Buffer        buffer )
   {
     FT_Error         error, retError = TTO_Err_Not_Covered;
     TTO_GPOSHeader*  gpos = gpi->gpos;
 
     FT_UShort*  properties = gpos->LookupList.Properties;
-    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;
+    buffer->in_pos = 0;
 
-    while ( in->pos < in->length )
+    while ( buffer->in_pos < buffer->in_length )
     {
-      if ( ~p_in[in->pos] & properties[lookup_index] )
+      if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
       {
         /* 0xFFFF indicates that we don't have a context length yet. */
 
            It is up to the font designer to provide meaningful lookups and
            lookup order.                                                   */
 
-        error = Do_Glyph_Lookup( gpi, lookup_index, in, out,
+        error = Do_Glyph_Lookup( gpi, lookup_index, buffer,
                                  0xFFFF, nesting_level );
         if ( error && error != TTO_Err_Not_Covered )
           return error;
         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)++;
+        (buffer->in_pos)++;
       else
        retError = error;
     }
   }
 
 
+  static FT_Error  Position_CursiveChain ( OTL_Buffer     buffer )
+  {
+    FT_ULong   i, j;
+    OTL_Position positions = buffer->positions;
+
+    /* First handle all left-to-right connections */
+    for (j = 0; j < buffer->in_length; j--)
+    {
+      if (positions[j].cursive_chain > 0)
+       positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
+    }
+    
+    /* Then handle all right-to-left connections */
+    for (i = buffer->in_length; i > 0; i--)
+    {
+      j = i - 1;
+
+      if (positions[j].cursive_chain < 0)
+       positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
+    }
+    
+    return TT_Err_Ok;
+  }
+
   EXPORT_FUNC
   FT_Error  TT_GPOS_Add_Feature( TTO_GPOSHeader*  gpos,
                                  FT_UShort        feature_index,
     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.         */
 
   FT_Error  TT_GPOS_Apply_String( FT_Face            face,
                                   TTO_GPOSHeader*    gpos,
                                   FT_UShort          load_flags,
-                                  TTO_GSUB_String*   in,
-                                  TTO_GPOS_Data**    out,
+                                 OTL_Buffer         buffer,
                                   FT_Bool            dvi,
                                   FT_Bool            r2l )
   {
-    FT_Memory      memory = gpos->memory;
     FT_Error       error, retError = TTO_Err_Not_Covered;
     GPOS_Instance  gpi;
 
 
 
     if ( !face || !gpos ||
-         !in || in->length == 0 || in->pos >= in->length )
+         !buffer || buffer->in_length == 0 || buffer->in_pos >= buffer->in_length )
       return TT_Err_Invalid_Argument;
 
     properties = gpos->LookupList.Properties;
     gpi.r2l        = r2l;
     gpi.dvi        = dvi;
 
-    if ( *out )
-      FREE( *out );
-    if ( ALLOC_ARRAY( *out, in->length, TTO_GPOS_Data ) )
-      return error;
-
     for ( j = 0; j < gpos->LookupList.LookupCount; j++ )
       if ( !properties || properties[j] )
       {
-        error = Do_String_Lookup( &gpi, j, in, *out );
+        error = Do_String_Lookup( &gpi, j, buffer );
        if ( error )
          {
            if ( error != TTO_Err_Not_Covered )
          retError = error;
       }
 
+    error = Position_CursiveChain ( buffer );
+    if ( error )
+      return error;
+
     return retError;
   }
 
index 408bcb0..bb766c6 100644 (file)
@@ -758,26 +758,6 @@ extern "C" {
   typedef union TTO_GPOS_SubTable_  TTO_GPOS_SubTable;
 
 
-  /* This `string object' is much simpler compared to TTO_GSUB_String.
-     A call to TTO_GPOS_Apply_String() will allocate it.               */
-
-  struct TTO_GPOS_Data_
-  {
-    FT_Pos     x_pos;
-    FT_Pos     y_pos;
-    FT_Pos     x_advance;
-    FT_Pos     y_advance;
-    FT_UShort  back;            /* number of glyphs to go back
-                                   for drawing current glyph   */
-    FT_Bool    new_advance;     /* if set, the advance width values are
-                                   absolute, i.e., they won't be
-                                   added to the original glyph's value
-                                   but rather replace them.            */
-  };
-
-  typedef struct TTO_GPOS_Data_  TTO_GPOS_Data;
-
-
   /* finally, the GPOS API */
 
   /*  EXPORT_DEF
@@ -844,8 +824,7 @@ extern "C" {
   FT_Error  TT_GPOS_Apply_String( FT_Face           face,
                                   TTO_GPOSHeader*   gpos,
                                   FT_UShort         load_flags,
-                                  TTO_GSUB_String*  in,
-                                  TTO_GPOS_Data**   out,
+                                 OTL_Buffer        buffer,
                                   FT_Bool           dvi,
                                   FT_Bool           r2l );
 
index 96aaffa..0ec246d 100644 (file)
 #define GSUB_ID  Build_Extension_ID( 'G', 'S', 'U', 'B' )
 
 
-#define ADD_String( in, num_in, out, num_out, glyph_data, component, ligID ) \
-          ( ( error = TT_GSUB_Add_String( (in), (num_in),                    \
-                                          (out), (num_out),                  \
-                                          (glyph_data), (component), (ligID) \
-                                        ) ) != TT_Err_Ok )
+#define IN_GLYPH( pos )      buffer->in_string[(pos)].gindex
+#define IN_CURGLYPH( pos )   buffer->in_string[(pos) + buffer->in_pos].gindex
+#define IN_PROPERTIES( pos ) buffer->in_string[(pos)].properties
+#define IN_LIGID( pos )      buffer->in_string[(pos)].ligID
+
+
+#define ADD_String( buffer, num_in, num_out, glyph_data, component, ligID )             \
+          ( ( error = otl_buffer_add_output_glyphs( (buffer),                           \
+                                                   (num_in), (num_out),                \
+                                                    (glyph_data), (component), (ligID)  \
+                                                  ) ) != TT_Err_Ok )
+#define ADD_Glyph( buffer, glyph_index, component, ligID )                                      \
+          ( ( error = otl_buffer_add_output_glyph( (buffer),                             \
+                                                    (glyph_index), (component), (ligID)  \
+                                                  ) ) != TT_Err_Ok )
 
 
   static FT_Error  Do_Glyph_Lookup( TTO_GSUBHeader*   gsub,
                                     FT_UShort         lookup_index,
-                                    TTO_GSUB_String*  in,
-                                    TTO_GSUB_String*  out,
+                                   OTL_Buffer        buffer,
                                     FT_UShort         context_length,
                                     int               nesting_level );
 
    **********************/
 
 
-  /* The following function copies `num_out' elements from `glyph_data'
-     to `out', advancing the array pointer in the `in' structure by
-     `num_in' elements, and in `out' by `num_out' elements.  If the
-     string (resp. the properties) array in `out' is empty or too
-     small, it allocates resp. reallocates the string (and properties)
-     array.  Finally, it sets the `length' field of `out' equal to
-     `pos' of the `out' structure.
-
-     If `component' is 0xFFFF, the value `in->component[in->pos]'
-     will be copied `num_out' times, otherwise `component' itself will
-     be used to fill `out->component'.
-
-     If `ligID' is 0xFFFF, the value `in->lig_IDs[in->pos]' will be
-     copied `num_out' times, otherwise `ligID' itself will be used to
-     fill `out->ligIDs'.
-
-     The properties (if defined) for all replaced glyphs are taken
-     from the glyph at position `in->pos'.
-
-     The logClusters[] value for the glyph at position in->pos is used
-     for all replacement glyphs */
-
-  EXPORT_FUNC
-  FT_Error  TT_GSUB_Add_String( TTO_GSUB_String*  in,
-                                FT_UShort         num_in,
-                                TTO_GSUB_String*  out,
-                                FT_UShort         num_out,
-                                FT_UShort*        glyph_data,
-                                FT_UShort         component,
-                                FT_UShort         ligID )
-  {
-    FT_Memory memory = in->memory;
-    FT_Error  error;
-    FT_UShort i;
-    FT_UShort p_in;
-    FT_UShort*p_out;
-
-
-    /* sanity check */
-
-    if ( !in || !out ||
-         in->length == 0 || in->pos >= in->length ||
-         in->length < in->pos + num_in )
-      return TT_Err_Invalid_Argument;
-
-    if ( out->pos + num_out >= out->allocated )
-    {
-      FT_ULong  size = out->pos + num_out + 256L;
-
-
-      /* The following works because all fields in `out' must be
-         initialized to zero (including the `string' field) for the
-         first use.                                                 */
-
-      if ( REALLOC_ARRAY( out->string, out->allocated, size, FT_UShort ) )
-        return error;
-      if ( REALLOC_ARRAY( out->components, out->allocated, size, FT_UShort ) )
-        return error;
-      if ( REALLOC_ARRAY( out->ligIDs, out->allocated, size, FT_UShort ) )
-        return error;
-      if ( in->properties )
-        if ( REALLOC_ARRAY( out->properties, out->allocated, size, FT_UShort ) )
-          return error;
-      if ( REALLOC_ARRAY( out->logClusters, out->allocated, size, FT_Int ) )
-       return error;
-
-      out->allocated = size;
-    }
-
-    if ( num_out )
-    {
-      MEM_Copy( &out->string[out->pos], glyph_data,
-                num_out * sizeof ( FT_UShort ) );
-
-      if ( component == 0xFFFF )
-        component = in->components[in->pos];
-
-      p_out = out->components;
-
-      for ( i = out->pos; i < out->pos + num_out; i++ )
-       p_out[i] = component;
-
-      p_out = out->ligIDs;
-
-      if ( ligID == 0xFFFF )
-        ligID = in->ligIDs[in->pos];
-
-      for ( i = out->pos; i < out->pos + num_out; i++ )
-        p_out[i] = ligID;
-
-      if ( in->properties )
-      {
-        p_in  = in->properties[in->pos];
-        p_out = out->properties;
-
-        for ( i = out->pos; i < out->pos + num_out; i++ )
-          p_out[i] = p_in;
-      }
-
-      for ( i = out->pos; i < out->pos + num_out; i++ )
-       out->logClusters[i] = in->logClusters[in->pos];
-    }
-
-    in->pos  += num_in;
-    out->pos += num_out;
-
-    out->length = out->pos;
-
-    return TT_Err_Ok;
-  }
-
-
-#if 0
-
-  /**********************
-   * Extension Functions
-   **********************/
-
-
-  static FT_Error  GSUB_Create( void*  ext,
-                                PFace  face )
-  {
-    DEFINE_LOAD_LOCALS( face->stream );
-
-    TTO_GSUBHeader*  gsub = (TTO_GSUBHeader*)ext;
-    Long             table;
-
-
-    /* by convention */
-
-    if ( !gsub )
-      return TT_Err_Ok;
-
-    /* a null offset indicates that there is no GSUB table */
-
-    gsub->offset = 0;
-
-    /* we store the start offset and the size of the subtable */
-
-    table = TT_LookUp_Table( face, TTAG_GSUB );
-    if ( table < 0 )
-      return TT_Err_Ok;             /* The table is optional */
-
-    if ( FILE_Seek( face->dirTables[table].Offset ) ||
-         ACCESS_Frame( 4L ) )
-      return error;
-
-    gsub->offset  = FILE_Pos() - 4L;    /* undo ACCESS_Frame() */
-    gsub->Version = GET_ULong();
-
-    FORGET_Frame();
-
-    gsub->loaded = FALSE;
-
-    return TT_Err_Ok;
-  }
-
-
-  static FT_Error  GSUB_Destroy( void*  ext,
-                                 PFace  face )
-  {
-    TTO_GSUBHeader*  gsub = (TTO_GSUBHeader*)ext;
-
-
-    /* by convention */
-
-    if ( !gsub )
-      return TT_Err_Ok;
-
-    if ( gsub->loaded )
-    {
-      Free_LookupList( &gsub->LookupList, GSUB, memory );
-      Free_FeatureList( &gsub->FeatureList, memory );
-      Free_ScriptList( &gsub->ScriptList, memory );
-    }
-
-    return TT_Err_Ok;
-  }
-
-
-  EXPORT_FUNC
-  FT_Error  TT_Init_GSUB_Extension( TT_Engine  engine )
-  {
-    PEngine_Instance  _engine = HANDLE_Engine( engine );
-
-
-    if ( !_engine )
-      return TT_Err_Invalid_Engine;
-
-    return  TT_Register_Extension( _engine,
-                                   GSUB_ID,
-                                   sizeof ( TTO_GSUBHeader ),
-                                   GSUB_Create,
-                                   GSUB_Destroy );
-  }
-#endif
-
   EXPORT_FUNC
   FT_Error  TT_Load_GSUB_Table( FT_Face          face,
                                 TTO_GSUBHeader** retptr,
 
 
   static FT_Error  Lookup_SingleSubst( TTO_SingleSubst*  ss,
-                                       TTO_GSUB_String*  in,
-                                       TTO_GSUB_String*  out,
+                                      OTL_Buffer        buffer,
                                        FT_UShort         flags,
                                        FT_UShort         context_length,
                                        TTO_GDEFHeader*   gdef )
   {
-    FT_UShort index, value[1], property;
+    FT_UShort index, value, property;
     FT_Error  error;
 
 
     if ( context_length != 0xFFFF && context_length < 1 )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &ss->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ss->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     switch ( ss->SubstFormat )
     {
     case 1:
-      value[0] = ( in->string[in->pos] + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF;
-      if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) )
+           value = ( IN_CURGLYPH( 0 ) + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF;
+      if ( ADD_Glyph( buffer, value, 0xFFFF, 0xFFFF ) )
         return error;
       break;
 
     case 2:
       if ( index >= ss->ssf.ssf2.GlyphCount )
         return TTO_Err_Invalid_GSUB_SubTable;
-      value[0] = ss->ssf.ssf2.Substitute[index];
-      if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) )
+      value = ss->ssf.ssf2.Substitute[index];
+      if ( ADD_Glyph( buffer, value, 0xFFFF, 0xFFFF ) )
         return error;
       break;
 
     {
       /* we inherit the old glyph class to the substituted glyph */
 
-      error = Add_Glyph_Property( gdef, value[0], property );
+      error = Add_Glyph_Property( gdef, value, property );
       if ( error && error != TTO_Err_Not_Covered )
         return error;
     }
 
 
   static FT_Error  Lookup_MultipleSubst( TTO_MultipleSubst*  ms,
-                                         TTO_GSUB_String*    in,
-                                         TTO_GSUB_String*    out,
+                                        OTL_Buffer          buffer,
                                          FT_UShort           flags,
                                          FT_UShort           context_length,
                                          TTO_GDEFHeader*     gdef )
     if ( context_length != 0xFFFF && context_length < 1 )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &ms->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ms->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     count = ms->Sequence[index].GlyphCount;
     s     = ms->Sequence[index].Substitute;
 
-    if ( ADD_String( in, 1, out, count, s, 0xFFFF, 0xFFFF ) )
+    if ( ADD_String( buffer, 1, count, s, 0xFFFF, 0xFFFF ) )
       return error;
 
     if ( gdef && gdef->NewGlyphClasses )
 
   static FT_Error  Lookup_AlternateSubst( TTO_GSUBHeader*      gsub,
                                           TTO_AlternateSubst*  as,
-                                          TTO_GSUB_String*     in,
-                                          TTO_GSUB_String*     out,
+                                         OTL_Buffer           buffer,
                                           FT_UShort            flags,
                                           FT_UShort            context_length,
                                           TTO_GDEFHeader*      gdef )
     if ( context_length != 0xFFFF && context_length < 1 )
       return TTO_Err_Not_Covered;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &as->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &as->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     /* we use a user-defined callback function to get the alternate index */
 
     if ( gsub->altfunc )
-      alt_index = (gsub->altfunc)( out->pos, in->string[in->pos],
+      alt_index = (gsub->altfunc)( buffer->out_pos, IN_CURGLYPH( 0 ),
                                    aset.GlyphCount, aset.Alternate,
                                    gsub->data );
     else
       alt_index = 0;
 
-    if ( ADD_String( in, 1, out, 1, &aset.Alternate[alt_index],
-                     0xFFFF, 0xFFFF ) )
+    if ( ADD_Glyph( buffer, aset.Alternate[alt_index],
+                   0xFFFF, 0xFFFF ) )
       return error;
 
     if ( gdef && gdef->NewGlyphClasses )
 
 
   static FT_Error  Lookup_LigatureSubst( TTO_LigatureSubst*  ls,
-                                         TTO_GSUB_String*    in,
-                                         TTO_GSUB_String*    out,
+                                        OTL_Buffer          buffer,
                                          FT_UShort           flags,
                                          FT_UShort           context_length,
                                          TTO_GDEFHeader*     gdef )
     FT_UShort      index, property;
     FT_Error       error;
     FT_UShort      numlig, i, j, is_mark, first_is_mark = FALSE;
-    FT_UShort*     s_in;
     FT_UShort*     c;
 
     TTO_Ligature*  lig;
 
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     if ( property == TTO_MARK || property & IGNORE_SPECIAL_MARKS )
       first_is_mark = TRUE;
 
-    error = Coverage_Index( &ls->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ls->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
           numlig;
           numlig--, lig++ )
     {
-      if ( in->pos + lig->ComponentCount > in->length )
+      if ( buffer->in_pos + lig->ComponentCount > buffer->in_length )
         continue;                         /* Not enough glyphs in input */
 
-      s_in = &in->string[in->pos];
       c    = lig->Component;
 
       is_mark = first_is_mark;
 
       for ( i = 1, j = 1; i < lig->ComponentCount; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( in->pos + j < in->length )
+          if ( buffer->in_pos + j < buffer->in_length )
             j++;
           else
             break;
         if ( !( property == TTO_MARK || property & IGNORE_SPECIAL_MARKS ) )
           is_mark = FALSE;
 
-        if ( s_in[j] != c[i - 1] )
+        if ( IN_CURGLYPH( j ) != c[i - 1] )
           break;
       }
 
           /* We don't use a new ligature ID if there are no skipped
              glyphs and the ligature already has an ID.             */
 
-          if ( in->ligIDs[in->pos] )
+          if ( IN_LIGID( buffer->in_pos ) )
           {
-            if ( ADD_String( in, i, out, 1, &lig->LigGlyph,
-                             0xFFFF, 0xFFFF ) )
+            if ( ADD_String( buffer, i, 1, &lig->LigGlyph,
+                           0xFFFF, 0xFFFF ) )
               return error;
           }
           else
           {
-            if ( ADD_String( in, i, out, 1, &lig->LigGlyph,
-                             0xFFFF, in->max_ligID ) )
+           FT_UShort ligID = otl_buffer_allocate_ligid( buffer );
+            if ( ADD_String( buffer, i, 1, &lig->LigGlyph,
+                           0xFFFF, ligID ) )
               return error;
-
-            (in->max_ligID)++;
           }
         }
         else
         {
-          if ( ADD_String( in, 1, out, 1, &lig->LigGlyph,
-                           0xFFFF, in->max_ligID ) )
+         FT_UShort ligID = otl_buffer_allocate_ligid( buffer );
+          if ( ADD_Glyph( buffer, lig->LigGlyph,
+                         0xFFFF, ligID ) )
             return error;
 
           /* Now we must do a second loop to copy the skipped glyphs to
 
           for ( i = 0; i < lig->ComponentCount - 1; i++ )
           {
-            while ( CHECK_Property( gdef, in->string[in->pos],
+            while ( CHECK_Property( gdef, IN_CURGLYPH( 0 ),
                                     flags, &property ) )
-              if ( ADD_String( in, 1, out, 1, &in->string[in->pos],
-                               i, in->max_ligID ) )
+              if ( ADD_Glyph( buffer, IN_CURGLYPH( 0 ),
+                             i, ligID ) )
                 return error;
 
-            (in->pos)++;
+            (buffer->in_pos)++;
           }
-
-          (in->max_ligID)++;
         }
 
         return TT_Err_Ok;
                                     FT_UShort               GlyphCount,
                                     FT_UShort               SubstCount,
                                     TTO_SubstLookupRecord*  subst,
-                                    TTO_GSUB_String*        in,
-                                    TTO_GSUB_String*        out,
+                                   OTL_Buffer              buffer,
                                     int                     nesting_level )
   {
     FT_Error  error;
     {
       if ( SubstCount && i == subst->SequenceIndex )
       {
-        old_pos = in->pos;
+        old_pos = buffer->in_pos;
 
         /* Do a substitution */
 
-        error = Do_Glyph_Lookup( gsub, subst->LookupListIndex, in, out,
+        error = Do_Glyph_Lookup( gsub, subst->LookupListIndex, buffer,
                                  GlyphCount, nesting_level );
 
         subst++;
         SubstCount--;
-        i += in->pos - old_pos;
+        i += buffer->in_pos - old_pos;
 
         if ( error == TTO_Err_Not_Covered )
         {
           /* XXX "can't happen" -- but don't count on it */
 
-          if ( ADD_String( in, 1, out, 1, &in->string[in->pos],
-                           0xFFFF, 0xFFFF ) )
+          if ( ADD_Glyph( buffer, IN_CURGLYPH( 0 ),
+                         0xFFFF, 0xFFFF ) )
             return error;
           i++;
         }
       {
         /* No substitution for this index */
 
-        if ( ADD_String( in, 1, out, 1, &in->string[in->pos],
-                         0xFFFF, 0xFFFF ) )
+        if ( ADD_Glyph( buffer, IN_CURGLYPH( 0 ),
+                       0xFFFF, 0xFFFF ) )
           return error;
         i++;
       }
   static FT_Error  Lookup_ContextSubst1(
                      TTO_GSUBHeader*           gsub,
                      TTO_ContextSubstFormat1*  csf1,
-                     TTO_GSUB_String*          in,
-                     TTO_GSUB_String*          out,
+                    OTL_Buffer                buffer,
                      FT_UShort                 flags,
                      FT_UShort                 context_length,
                      int                       nesting_level )
     FT_UShort        index, property;
     FT_UShort        i, j, k, numsr;
     FT_Error         error;
-    FT_UShort*       s_in;
 
     TTO_SubRule*     sr;
     TTO_GDEFHeader*  gdef;
 
     gdef = gsub->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &csf1->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &csf1->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
       if ( context_length != 0xFFFF && context_length < sr[k].GlyphCount )
         continue;
 
-      if ( in->pos + sr[k].GlyphCount > in->length )
+      if ( buffer->in_pos + sr[k].GlyphCount > buffer->in_length )
         continue;                           /* context is too long */
 
-      s_in = &in->string[in->pos];
-
       for ( i = 1, j = 1; i < sr[k].GlyphCount; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( in->pos + j < in->length )
+          if ( buffer->in_pos + j < buffer->in_length )
             j++;
           else
             break;
         }
 
-        if ( s_in[j] != sr[k].Input[i - 1] )
+        if ( IN_CURGLYPH( j ) != sr[k].Input[i - 1] )
           break;
       }
 
       if ( i == sr[k].GlyphCount )
         return Do_ContextSubst( gsub, sr[k].GlyphCount,
                                 sr[k].SubstCount, sr[k].SubstLookupRecord,
-                                in, out,
+                               buffer,
                                 nesting_level );
     }
 
   static FT_Error  Lookup_ContextSubst2(
                      TTO_GSUBHeader*           gsub,
                      TTO_ContextSubstFormat2*  csf2,
-                     TTO_GSUB_String*          in,
-                     TTO_GSUB_String*          out,
+                    OTL_Buffer                buffer,
                      FT_UShort                 flags,
                      FT_UShort                 context_length,
                      int                       nesting_level )
     FT_UShort          i, j, k, known_classes;
 
     FT_UShort*         classes;
-    FT_UShort*         s_in;
     FT_UShort*         cl;
 
     TTO_SubClassSet*   scs;
 
     gdef = gsub->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     /* Note: The coverage table in format 2 doesn't give an index into
              anything.  It just lets us know whether or not we need to
              do any lookup at all.                                     */
 
-    error = Coverage_Index( &csf2->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &csf2->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
     if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, FT_UShort ) )
       return error;
 
-    error = Get_Class( &csf2->ClassDef, in->string[in->pos],
+    error = Get_Class( &csf2->ClassDef, IN_CURGLYPH( 0 ),
                        &classes[0], NULL );
     if ( error && error != TTO_Err_Not_Covered )
       goto End;
       if ( context_length != 0xFFFF && context_length < sr->GlyphCount )
         continue;
 
-      if ( in->pos + sr->GlyphCount > in->length )
+      if ( buffer->in_pos + sr->GlyphCount > buffer->in_length )
         continue;                           /* context is too long */
 
-      s_in = &in->string[in->pos];
       cl   = sr->Class;
 
       /* Start at 1 because [0] is implied */
 
       for ( i = 1, j = 1; i < sr->GlyphCount; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             goto End;
 
-          if ( in->pos + j < in->length )
+          if ( buffer->in_pos + j < buffer->in_length )
             j++;
           else
             break;
         {
           /* Keeps us from having to do this for each rule */
 
-          error = Get_Class( &csf2->ClassDef, s_in[j], &classes[i], NULL );
+          error = Get_Class( &csf2->ClassDef, IN_CURGLYPH( j ), &classes[i], NULL );
           if ( error && error != TTO_Err_Not_Covered )
             goto End;
           known_classes = i;
       {
         error = Do_ContextSubst( gsub, sr->GlyphCount,
                                  sr->SubstCount, sr->SubstLookupRecord,
-                                 in, out,
+                                buffer,
                                  nesting_level );
         goto End;
       }
   static FT_Error  Lookup_ContextSubst3(
                      TTO_GSUBHeader*           gsub,
                      TTO_ContextSubstFormat3*  csf3,
-                     TTO_GSUB_String*          in,
-                     TTO_GSUB_String*          out,
+                    OTL_Buffer                buffer,
                      FT_UShort                 flags,
                      FT_UShort                 context_length,
                      int                       nesting_level )
   {
     FT_Error         error;
     FT_UShort        index, i, j, property;
-    FT_UShort*       s_in;
 
     TTO_Coverage*    c;
     TTO_GDEFHeader*  gdef;
 
     gdef = gsub->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     if ( context_length != 0xFFFF && context_length < csf3->GlyphCount )
       return TTO_Err_Not_Covered;
 
-    if ( in->pos + csf3->GlyphCount > in->length )
+    if ( buffer->in_pos + csf3->GlyphCount > buffer->in_length )
       return TTO_Err_Not_Covered;         /* context is too long */
 
-    s_in = &in->string[in->pos];
     c    = csf3->Coverage;
 
     for ( i = 1, j = 1; i < csf3->GlyphCount; i++, j++ )
     {
-      while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+      while ( CHECK_Property( gdef, IN_CURGLYPH( j ), flags, &property ) )
       {
         if ( error && error != TTO_Err_Not_Covered )
           return error;
 
-        if ( in->pos + j < in->length )
+        if ( buffer->in_pos + j < buffer->in_length )
           j++;
         else
           return TTO_Err_Not_Covered;
       }
 
-      error = Coverage_Index( &c[i], s_in[j], &index );
+      error = Coverage_Index( &c[i], IN_CURGLYPH( j ), &index );
       if ( error )
         return error;
     }
 
     return Do_ContextSubst( gsub, csf3->GlyphCount,
                             csf3->SubstCount, csf3->SubstLookupRecord,
-                            in, out,
+                           buffer,
                             nesting_level );
   }
 
 
   static FT_Error  Lookup_ContextSubst( TTO_GSUBHeader*    gsub,
                                         TTO_ContextSubst*  cs,
-                                        TTO_GSUB_String*   in,
-                                        TTO_GSUB_String*   out,
+                                       OTL_Buffer         buffer,
                                         FT_UShort          flags,
                                         FT_UShort          context_length,
                                         int                nesting_level )
     switch ( cs->SubstFormat )
     {
     case 1:
-      return Lookup_ContextSubst1( gsub, &cs->csf.csf1, in, out,
+      return Lookup_ContextSubst1( gsub, &cs->csf.csf1, buffer,
                                    flags, context_length, nesting_level );
 
     case 2:
-      return Lookup_ContextSubst2( gsub, &cs->csf.csf2, in, out,
+      return Lookup_ContextSubst2( gsub, &cs->csf.csf2, buffer,
                                    flags, context_length, nesting_level );
 
     case 3:
-      return Lookup_ContextSubst3( gsub, &cs->csf.csf3, in, out,
+      return Lookup_ContextSubst3( gsub, &cs->csf.csf3, buffer,
                                    flags, context_length, nesting_level );
 
     default:
   static FT_Error  Lookup_ChainContextSubst1(
                      TTO_GSUBHeader*                gsub,
                      TTO_ChainContextSubstFormat1*  ccsf1,
-                     TTO_GSUB_String*               in,
-                     TTO_GSUB_String*               out,
+                    OTL_Buffer                     buffer,
                      FT_UShort                      flags,
                      FT_UShort                      context_length,
                      int                            nesting_level )
     FT_UShort          i, j, k, num_csr, curr_pos;
     FT_UShort          bgc, igc, lgc;
     FT_Error           error;
-    FT_UShort*         s_in;
 
     TTO_ChainSubRule*  csr;
     TTO_ChainSubRule   curr_csr;
 
     gdef = gsub->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
-    error = Coverage_Index( &ccsf1->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ccsf1->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
 
       /* check whether context is too long; it is a first guess only */
 
-      if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+      if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
         continue;
 
       if ( bgc )
            we search backwards for matches in the backtrack glyph array    */
 
         curr_pos = 0;
-        s_in     = &in->string[curr_pos];
 
-        for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+        for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
         {
-          while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+          while ( CHECK_Property( gdef, IN_GLYPH( j ), flags, &property ) )
           {
             if ( error && error != TTO_Err_Not_Covered )
               return error;
                Backtrack offsets -  3  2  1  0
                Lookahead offsets -                    0  1  2  3           */
 
-          if ( s_in[j] != curr_csr.Backtrack[i] )
+          if ( IN_GLYPH( j ) != curr_csr.Backtrack[i] )
             break;
         }
 
           continue;
       }
 
-      curr_pos = in->pos;
-      s_in     = &in->string[curr_pos];
+      curr_pos = buffer->in_pos;
 
       /* Start at 1 because [0] is implied */
 
       for ( i = 1, j = 1; i < igc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
         }
 
-        if ( s_in[j] != curr_csr.Input[i - 1] )
+        if ( IN_GLYPH( curr_pos + j ) != curr_csr.Input[i - 1] )
           break;
       }
 
          last context glyph                                            */
 
       curr_pos += j;
-      s_in     = &in->string[curr_pos];
 
       for ( i = 0, j = 0; i < lgc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
         }
 
-        if ( s_in[j] != curr_csr.Lookahead[i] )
+        if ( IN_GLYPH( curr_pos + j ) != curr_csr.Lookahead[i] )
           break;
       }
 
         return Do_ContextSubst( gsub, igc,
                                 curr_csr.SubstCount,
                                 curr_csr.SubstLookupRecord,
-                                in, out,
+                                buffer,
                                 nesting_level );
     }
 
   static FT_Error  Lookup_ChainContextSubst2(
                      TTO_GSUBHeader*                gsub,
                      TTO_ChainContextSubstFormat2*  ccsf2,
-                     TTO_GSUB_String*               in,
-                     TTO_GSUB_String*               out,
+                    OTL_Buffer                     buffer,
                      FT_UShort                      flags,
                      FT_UShort                      context_length,
                      int                            nesting_level )
     FT_UShort*             input_classes;
     FT_UShort*             lookahead_classes;
 
-    FT_UShort*             s_in;
-
     FT_UShort*             bc;
     FT_UShort*             ic;
     FT_UShort*             lc;
     gdef = gsub->gdef;
     memory = gsub->memory;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     /* Note: The coverage table in format 2 doesn't give an index into
              anything.  It just lets us know whether or not we need to
              do any lookup at all.                                     */
 
-    error = Coverage_Index( &ccsf2->Coverage, in->string[in->pos], &index );
+    error = Coverage_Index( &ccsf2->Coverage, IN_CURGLYPH( 0 ), &index );
     if ( error )
       return error;
 
       goto End2;
     known_lookahead_classes = 0;
 
-    error = Get_Class( &ccsf2->InputClassDef, in->string[in->pos],
+    error = Get_Class( &ccsf2->InputClassDef, IN_CURGLYPH( 0 ),
                        &input_classes[0], NULL );
     if ( error && error != TTO_Err_Not_Covered )
       goto End1;
 
       /* check whether context is too long; it is a first guess only */
 
-      if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+      if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
         continue;
 
       if ( bgc )
            Note that `known_backtrack_classes' starts at index 0.         */
 
         curr_pos = 0;
-        s_in     = &in->string[curr_pos];
         bc       = ccsr.Backtrack;
 
-        for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+        for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
         {
-          while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+          while ( CHECK_Property( gdef, IN_GLYPH( j ), flags, &property ) )
           {
             if ( error && error != TTO_Err_Not_Covered )
               goto End1;
           {
             /* Keeps us from having to do this for each rule */
 
-            error = Get_Class( &ccsf2->BacktrackClassDef, s_in[j],
+            error = Get_Class( &ccsf2->BacktrackClassDef, IN_GLYPH( j ),
                                &backtrack_classes[i], NULL );
             if ( error && error != TTO_Err_Not_Covered )
               goto End1;
           continue;
       }
 
-      curr_pos = in->pos;
-      s_in     = &in->string[curr_pos];
+      curr_pos = buffer->in_pos;
       ic       = ccsr.Input;
 
       /* Start at 1 because [0] is implied */
 
       for ( i = 1, j = 1; i < igc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
 
         if ( i >= known_input_classes )
         {
-          error = Get_Class( &ccsf2->InputClassDef, s_in[j],
+          error = Get_Class( &ccsf2->InputClassDef, IN_GLYPH( curr_pos + j ),
                              &input_classes[i], NULL );
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
          last context glyph                                            */
 
       curr_pos += j;
-      s_in     = &in->string[curr_pos];
       lc       = ccsr.Lookahead;
 
       for ( i = 0, j = 0; i < lgc; i++, j++ )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
 
-          if ( curr_pos + j < in->length )
+          if ( curr_pos + j < buffer->in_length )
             j++;
           else
             break;
 
         if ( i >= known_lookahead_classes )
         {
-          error = Get_Class( &ccsf2->LookaheadClassDef, s_in[j],
+          error = Get_Class( &ccsf2->LookaheadClassDef, IN_GLYPH( curr_pos + j ),
                              &lookahead_classes[i], NULL );
           if ( error && error != TTO_Err_Not_Covered )
             goto End1;
         error = Do_ContextSubst( gsub, igc,
                                  ccsr.SubstCount,
                                  ccsr.SubstLookupRecord,
-                                 in, out,
+                                 buffer,
                                  nesting_level );
         goto End1;
       }
   static FT_Error  Lookup_ChainContextSubst3(
                      TTO_GSUBHeader*                gsub,
                      TTO_ChainContextSubstFormat3*  ccsf3,
-                     TTO_GSUB_String*               in,
-                     TTO_GSUB_String*               out,
+                    OTL_Buffer                     buffer,
                      FT_UShort                      flags,
                      FT_UShort                      context_length,
                      int                            nesting_level )
     FT_UShort        index, i, j, curr_pos, property;
     FT_UShort        bgc, igc, lgc;
     FT_Error         error;
-    FT_UShort*       s_in;
 
     TTO_Coverage*    bc;
     TTO_Coverage*    ic;
 
     gdef = gsub->gdef;
 
-    if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+    if ( CHECK_Property( gdef, IN_CURGLYPH( 0 ), flags, &property ) )
       return error;
 
     bgc = ccsf3->BacktrackGlyphCount;
 
     /* check whether context is too long; it is a first guess only */
 
-    if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+    if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
       return TTO_Err_Not_Covered;
 
     if ( bgc )
          we search backwards for matches in the backtrack glyph array    */
 
       curr_pos = 0;
-      s_in     = &in->string[curr_pos];
       bc       = ccsf3->BacktrackCoverage;
 
-      for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+      for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
       {
-        while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+        while ( CHECK_Property( gdef, IN_GLYPH( j ), flags, &property ) )
         {
           if ( error && error != TTO_Err_Not_Covered )
             return error;
             return TTO_Err_Not_Covered;
         }
 
-        error = Coverage_Index( &bc[i], s_in[j], &index );
+        error = Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
         if ( error )
           return error;
       }
     }
 
-    curr_pos = in->pos;
-    s_in     = &in->string[curr_pos];
+    curr_pos = buffer->in_pos;
     ic       = ccsf3->InputCoverage;
 
     for ( i = 0, j = 0; i < igc; i++, j++ )
     {
-      /* We already called CHECK_Property for s_in[0] */
-      while ( j > 0 && CHECK_Property( gdef, s_in[j], flags, &property ) )
+      /* We already called CHECK_Property for IN_GLYPH( curr_pos ) */
+      while ( j > 0 && CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
       {
         if ( error && error != TTO_Err_Not_Covered )
           return error;
 
-        if ( curr_pos + j < in->length )
+        if ( curr_pos + j < buffer->in_length )
           j++;
         else
           return TTO_Err_Not_Covered;
       }
 
-      error = Coverage_Index( &ic[i], s_in[j], &index );
+      error = Coverage_Index( &ic[i], IN_GLYPH( curr_pos + j ), &index );
       if ( error )
         return error;
     }
        glyph                                                             */
 
     curr_pos += j;
-    s_in     = &in->string[curr_pos];
     lc       = ccsf3->LookaheadCoverage;
 
     for ( i = 0, j = 0; i < lgc; i++, j++ )
     {
-      while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+      while ( CHECK_Property( gdef, IN_GLYPH( curr_pos + j ), flags, &property ) )
       {
         if ( error && error != TTO_Err_Not_Covered )
           return error;
 
-        if ( curr_pos + j < in->length )
+        if ( curr_pos + j < buffer->in_length )
           j++;
         else
           return TTO_Err_Not_Covered;
       }
 
-      error = Coverage_Index( &lc[i], s_in[j], &index );
+      error = Coverage_Index( &lc[i], IN_GLYPH( curr_pos + j ), &index );
       if ( error )
         return error;
     }
     return Do_ContextSubst( gsub, igc,
                             ccsf3->SubstCount,
                             ccsf3->SubstLookupRecord,
-                            in, out,
+                           buffer,
                             nesting_level );
   }
 
   static FT_Error  Lookup_ChainContextSubst(
                      TTO_GSUBHeader*         gsub,
                      TTO_ChainContextSubst*  ccs,
-                     TTO_GSUB_String*        in,
-                     TTO_GSUB_String*        out,
+                    OTL_Buffer              buffer,
                      FT_UShort               flags,
                      FT_UShort               context_length,
                      int                     nesting_level )
     switch ( ccs->SubstFormat )
     {
     case 1:
-      return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, in, out,
+      return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, buffer,
                                         flags, context_length,
                                         nesting_level );
 
     case 2:
-      return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, in, out,
+      return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, buffer,
                                         flags, context_length,
                                         nesting_level );
 
     case 3:
-      return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, in, out,
+      return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, buffer,
                                         flags, context_length,
                                         nesting_level );
 
 
   static FT_Error  Do_Glyph_Lookup( TTO_GSUBHeader*   gsub,
                                     FT_UShort         lookup_index,
-                                    TTO_GSUB_String*  in,
-                                    TTO_GSUB_String*  out,
+                                   OTL_Buffer        buffer,
                                     FT_UShort         context_length,
                                     int               nesting_level )
   {
       {
       case GSUB_LOOKUP_SINGLE:
         error = Lookup_SingleSubst( &lo->SubTable[i].st.gsub.single,
-                                    in, out,
+                                    buffer,
                                     flags, context_length, gsub->gdef );
         break;
 
       case GSUB_LOOKUP_MULTIPLE:
         error = Lookup_MultipleSubst( &lo->SubTable[i].st.gsub.multiple,
-                                      in, out,
+                                      buffer,
                                       flags, context_length, gsub->gdef );
         break;
 
       case GSUB_LOOKUP_ALTERNATE:
         error = Lookup_AlternateSubst( gsub,
                                        &lo->SubTable[i].st.gsub.alternate,
-                                       in, out,
+                                       buffer,
                                        flags, context_length, gsub->gdef );
         break;
 
       case GSUB_LOOKUP_LIGATURE:
         error = Lookup_LigatureSubst( &lo->SubTable[i].st.gsub.ligature,
-                                      in, out,
+                                     buffer,
                                       flags, context_length, gsub->gdef );
         break;
 
       case GSUB_LOOKUP_CONTEXT:
         error = Lookup_ContextSubst( gsub, &lo->SubTable[i].st.gsub.context,
-                                     in, out,
+                                     buffer,
                                      flags, context_length, nesting_level );
         break;
 
       case GSUB_LOOKUP_CHAIN:
         error = Lookup_ChainContextSubst( gsub,
                                           &lo->SubTable[i].st.gsub.chain,
-                                          in, out,
+                                         buffer,
                                           flags, context_length,
                                           nesting_level );
         break;
     return TTO_Err_Not_Covered;
   }
 
-
   /* apply one lookup to the input string object */
 
   static FT_Error  Do_String_Lookup( TTO_GSUBHeader*   gsub,
                                      FT_UShort         lookup_index,
-                                     TTO_GSUB_String*  in,
-                                     TTO_GSUB_String*  out )
+                                    OTL_Buffer        buffer )
   {
     FT_Error  error, retError = TTO_Err_Not_Covered;
 
     FT_UShort*  properties = gsub->LookupList.Properties;
-    FT_UShort*  p_in       = in->properties;
-    FT_UShort*  s_in       = in->string;
 
     int      nesting_level = 0;
 
 
-    while ( in->pos < in->length )
+    while ( buffer->in_pos < buffer->in_length )
     {
-      if ( ~p_in[in->pos] & properties[lookup_index] )
+      if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
       {
         /* 0xFFFF indicates that we don't have a context length yet */
-        error = Do_Glyph_Lookup( gsub, lookup_index, in, out,
+        error = Do_Glyph_Lookup( gsub, lookup_index, buffer,
                                  0xFFFF, nesting_level );
         if ( error )
        {
         error = TTO_Err_Not_Covered;
 
       if ( error == TTO_Err_Not_Covered ) 
-        if ( ADD_String( in, 1, out, 1, &s_in[in->pos], 0xFFFF, 0xFFFF ) )
+        if ( ADD_Glyph( buffer, IN_CURGLYPH( 0 ), 0xFFFF, 0xFFFF ) )
           return error;
     }
 
 
 
   EXPORT_FUNC
-  FT_Error  TT_GSUB_String_New( FT_Memory           memory,
-                               TTO_GSUB_String   **result )
-  {
-    FT_Error error;
-    
-    TTO_GSUB_String *str;
-
-    if ( ALLOC( str, sizeof( *str ) ) )
-      return error;
-
-    str->memory = memory;
-
-    str->length = 0;
-    str->allocated = 0;
-    str->pos = 0;
-    str->string = NULL;
-    str->properties = NULL;
-    str->components = NULL;
-    str->max_ligID = 0;
-    str->ligIDs = 0;
-    str->logClusters = 0;
-
-    *result = str;
-
-    return TT_Err_Ok;
-  }
-
-  EXPORT_DEF
-  FT_Error  TT_GSUB_String_Set_Length( TTO_GSUB_String *str,
-                                      FT_ULong         new_length)
-  {
-    FT_Memory memory = str->memory;
-    FT_Error error;
-    
-    if ( new_length > str->allocated )
-    {
-      if ( REALLOC_ARRAY( str->string, str->allocated, new_length, FT_UShort ) )
-        return error;
-      if ( REALLOC_ARRAY( str->properties, str->allocated, new_length, FT_UShort ) )
-        return error;
-      if ( REALLOC_ARRAY( str->components, str->allocated, new_length, FT_UShort ) )
-        return error;
-      if ( REALLOC_ARRAY( str->ligIDs, str->allocated, new_length, FT_UShort ) )
-        return error;
-      if ( REALLOC_ARRAY( str->logClusters, str->allocated, new_length, FT_Int ) )
-        return error;
-
-      str->allocated = new_length;
-      str->length = new_length;
-    }
-
-    return TT_Err_Ok;
-  }
-
-  EXPORT_FUNC
-  FT_Error  TT_GSUB_String_Done( TTO_GSUB_String   *str )
-  {
-    FT_Memory memory = str->memory;
-
-    FREE( str->string );
-    FREE( str->properties );
-    FREE( str->components );
-    FREE( str->ligIDs );
-    FREE( str->logClusters );
-
-    FREE( str );
-
-    return TT_Err_Ok;
-  }
-
-  EXPORT_FUNC
   FT_Error  TT_GSUB_Apply_String( TTO_GSUBHeader*   gsub,
-                                  TTO_GSUB_String*  in,
-                                  TTO_GSUB_String*  out )
+                                 OTL_Buffer        buffer )
   {
     FT_Error          error, retError = TTO_Err_Not_Covered;
-    FT_Memory         memory = in->memory;
     FT_UShort         j;
 
-    TTO_GSUB_String   tmp1;
-    TTO_GSUB_String*  ptmp1;
-    TTO_GSUB_String   tmp2;
-    TTO_GSUB_String*  ptmp2;
-    TTO_GSUB_String*  t;
-
     FT_UShort*        properties;
 
-
     if ( !gsub ||
-         !in || !out || in->length == 0 || in->pos >= in->length )
+         !buffer || buffer->in_length == 0 || buffer->in_pos >= buffer->in_length )
       return TT_Err_Invalid_Argument;
 
     properties = gsub->LookupList.Properties;
     
-    tmp1.memory      = memory;
-    tmp1.length      = in->length;
-    tmp1.allocated   = in->length;
-    tmp1.pos         = in->pos;
-    tmp1.max_ligID   = 1;
-    tmp1.string      = NULL;
-    tmp1.properties  = NULL;
-    tmp1.components  = NULL;
-    tmp1.ligIDs      = NULL;
-    tmp1.logClusters = NULL;
-
-    tmp2.memory      = memory;
-    tmp2.allocated   = 0;
-    tmp2.pos         = 0;
-    tmp2.string      = NULL;
-    tmp2.properties  = NULL;
-    tmp2.components  = NULL;
-    tmp2.ligIDs      = NULL;
-    tmp2.logClusters = NULL;
-
-    ptmp1 = &tmp1;
-    ptmp2 = &tmp2;
-
-    if ( ALLOC_ARRAY( tmp1.string, tmp1.length, FT_UShort ) )
-      return error;
-    MEM_Copy( tmp1.string, in->string, in->length * sizeof ( FT_UShort ) );
-
-    /* make sure that we always have a `properties', `components', and
-       `ligIDs' array in the string object                             */
-
-    if ( ALLOC_ARRAY( tmp1.components, tmp1.length, FT_UShort ) )
-      goto End;
-    if ( ALLOC_ARRAY( tmp1.ligIDs, tmp1.length, FT_UShort ) )
-      goto End;
-    if ( ALLOC_ARRAY( tmp1.properties, tmp1.length, FT_UShort ) )
-      goto End;
-    if ( in->properties )
-      MEM_Copy( tmp1.properties, in->properties,
-                in->length * sizeof( FT_UShort ) );
-    if ( ALLOC_ARRAY( tmp1.logClusters, tmp1.length, FT_Int ) )
-      goto End;
-    MEM_Copy( tmp1.logClusters, in->logClusters,
-             in->length * sizeof( FT_Int ) );
-
     for ( j = 0; j < gsub->LookupList.LookupCount; j++ )
       if ( properties[j] )
       {
-        error = Do_String_Lookup( gsub, j, ptmp1, ptmp2 );
+        error = Do_String_Lookup( gsub, j, buffer );
         if ( error )
        {
          if ( error != TTO_Err_Not_Covered )
        else
          retError = error;
          
-
-        /* flipping `in' and `out', preparing the next loop */
-
-        ptmp1->pos       = in->pos;
-        ptmp2->length    = ptmp2->pos;
-        ptmp2->pos       = in->pos;
-        ptmp2->max_ligID = ptmp1->max_ligID;
-
-        t     = ptmp2;
-        ptmp2 = ptmp1;
-        ptmp1 = t;
+       error = otl_buffer_swap( buffer );
+       if ( error )
+         goto End;
       }
+    
+    error = retError;
 
   End:
-    FREE( ptmp2->string );
-    FREE( ptmp2->properties );
-    FREE( ptmp2->components );
-    FREE( ptmp2->ligIDs );
-    FREE( ptmp2->logClusters );
-
-    if ( error && error != TTO_Err_Not_Covered )
-    {
-      FREE( ptmp1->string );
-      FREE( ptmp1->components );
-      FREE( ptmp1->ligIDs );
-      FREE( ptmp1->properties );
-      FREE( ptmp1->logClusters );
-      
-      return error;
-    }
-    else
-    {
-      out->length      = ptmp1->length;
-      out->pos         = 0;
-      out->allocated   = ptmp1->allocated;
-      out->string      = ptmp1->string;
-      out->components  = ptmp1->components;
-      out->ligIDs      = ptmp1->ligIDs;
-      out->logClusters = ptmp1->logClusters;
-      
-      if ( in->properties )
-       out->properties = ptmp1->properties;
-      else
-      {
-       FREE( ptmp1->properties );
-       out->properties = NULL;
-      }
-
-      return retError;
-    }
+    return error;
   }
 
 
index 8951e44..51dda1c 100644 (file)
@@ -504,23 +504,6 @@ extern "C" {
      TT_Add_String() will also handle allocation; you should use
      free() in case you want to destroy the arrays in the object. */
 
-  struct  TTO_GSUB_String_
-  {
-    FT_Memory   memory;
-    
-    FT_ULong    length;
-    FT_ULong    pos;
-    FT_ULong    allocated;
-    FT_UShort*  string;
-    FT_UShort*  properties;
-    FT_UShort*  components;
-    FT_UShort   max_ligID;
-    FT_UShort*  ligIDs;
-    FT_Int*     logClusters;
-  };
-
-  typedef struct TTO_GSUB_String_  TTO_GSUB_String;
-
 
   /* finally, the GSUB API */
 
@@ -578,30 +561,9 @@ extern "C" {
                                                  void*            data );
   
   EXPORT_DEF
-  FT_Error  TT_GSUB_String_New( FT_Memory           memory,
-                               TTO_GSUB_String   **result );
-
-  EXPORT_DEF
-  FT_Error  TT_GSUB_String_Set_Length( TTO_GSUB_String *str,
-                                      FT_ULong         new_length);
-
-  EXPORT_DEF
-  FT_Error  TT_GSUB_String_Done( TTO_GSUB_String   *str );
-
-
-  EXPORT_DEF
   FT_Error  TT_GSUB_Apply_String( TTO_GSUBHeader*   gsub,
-                                  TTO_GSUB_String*  in,
-                                  TTO_GSUB_String*  out );
+                                 OTL_Buffer        buffer );
 
-  EXPORT_DEF
-  FT_Error  TT_GSUB_Add_String( TTO_GSUB_String*  in,
-                                FT_UShort         num_in,
-                                TTO_GSUB_String*  out,
-                                FT_UShort         num_out,
-                                FT_UShort*        glyph_data,
-                                FT_UShort         component,
-                                FT_UShort         ligID );
 
 #ifdef __cplusplus
 }
index 6788dec..482bcb5 100644 (file)
@@ -280,6 +280,7 @@ extern "C" {
   typedef struct TTO_Device_  TTO_Device;
 
 
+#include "otlbuffer.h"
 #include "ftxgdef.h"
 #include "ftxgsub.h"
 #include "ftxgpos.h"
diff --git a/src/otlbuffer.c b/src/otlbuffer.c
new file mode 100644 (file)
index 0000000..b35a63f
--- /dev/null
@@ -0,0 +1,213 @@
+/* otlbuffer.c: Buffer of glyphs for substitution/positioning
+ *
+ * Copyright 2004 Red Hat Software
+ *
+ * Portions Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ */
+#include <otlbuffer.h>
+
+#include FT_INTERNAL_MEMORY_H
+
+  static FT_Error
+  otl_buffer_ensure( OTL_Buffer buffer,
+                    FT_ULong   size )
+  {
+    FT_Memory memory = buffer->memory;
+    FT_ULong new_allocated = buffer->allocated;
+
+    if (size > new_allocated)
+      {
+       FT_Error error;
+
+       while (size > new_allocated)
+         new_allocated += (new_allocated >> 1) + 8;
+       
+       if ( FT_REALLOC_ARRAY( buffer->in_string, buffer->allocated, new_allocated, OTL_GlyphItemRec ) )
+         return error;
+       if ( FT_REALLOC_ARRAY( buffer->out_string, buffer->allocated, new_allocated, OTL_GlyphItemRec ) )
+         return error;
+       if ( FT_REALLOC_ARRAY( buffer->positions, buffer->allocated, new_allocated, OTL_PositionRec ) )
+         return error;
+
+       buffer->allocated = new_allocated;
+      }
+
+    return FT_Err_Ok;
+  }
+
+  FT_Error
+  otl_buffer_new( FT_Memory   memory,
+                 OTL_Buffer *buffer )
+  {
+    FT_Error error;
+    
+    if ( FT_ALLOC( *buffer, sizeof( OTL_BufferRec ) ) )
+      return error;
+
+    (*buffer)->memory = memory;
+    (*buffer)->in_length = 0;
+    (*buffer)->out_length = 0;
+    (*buffer)->allocated = 0;
+    (*buffer)->in_pos = 0;
+    (*buffer)->out_pos = 0;
+
+    (*buffer)->in_string = NULL;
+    (*buffer)->out_string = NULL;
+    (*buffer)->positions = NULL;
+    (*buffer)->max_ligID = 0;
+
+    return FT_Err_Ok;
+  }
+
+  FT_Error
+  otl_buffer_swap( OTL_Buffer buffer )
+  {
+    OTL_GlyphItem tmp_string;
+
+    tmp_string = buffer->in_string;
+    buffer->in_string = buffer->out_string;
+    buffer->out_string = tmp_string;
+
+    buffer->in_length = buffer->out_length;
+    buffer->out_length = 0;
+    
+    buffer->in_pos = 0;
+    buffer->out_pos = 0;
+
+    return FT_Err_Ok;
+  }
+
+  FT_Error
+  otl_buffer_free( OTL_Buffer buffer )
+  {
+    FT_Memory memory = buffer->memory;
+
+    FT_FREE( buffer->in_string );
+    FT_FREE( buffer->out_string );
+    FT_FREE( buffer );
+
+    return FT_Err_Ok;
+  }
+
+  FT_Error
+  otl_buffer_clear( OTL_Buffer buffer )
+  {
+    buffer->in_length = 0;
+    buffer->out_length = 0;
+    buffer->in_pos = 0;
+    buffer->out_pos = 0;
+    
+    return FT_Err_Ok;
+  }
+
+  FT_Error
+  otl_buffer_add_glyph( OTL_Buffer buffer,
+                       FT_UInt    glyph_index,
+                       FT_UInt    properties,
+                       FT_UInt    cluster )
+  {
+    FT_Error error;
+    OTL_GlyphItem glyph;
+    
+    error = otl_buffer_ensure( buffer, buffer->in_length + 1 );
+    if ( error != FT_Err_Ok )
+      return error;
+
+    glyph = &buffer->in_string[buffer->in_length];
+    glyph->gindex = glyph_index;
+    glyph->properties = properties;
+    glyph->cluster = cluster;
+    glyph->component = 0;
+    glyph->ligID = 0;
+    
+    buffer->in_length++;
+
+    return FT_Err_Ok;
+  }
+
+  /* The following function copies `num_out' elements from `glyph_data'
+     to `buffer->out_string', advancing the in array pointer in the structure
+     by `num_in' elements, and the out array pointer by `num_out' elements.
+     Finally, it sets the `length' field of `out' equal to
+     `pos' of the `out' structure.
+
+     If `component' is 0xFFFF, the component value from buffer->in_pos
+     will copied `num_out' times, otherwise `component' itself will
+     be used to fill the `component' fields.
+
+     If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
+     will copied `num_out' times, otherwise `ligID' itself will
+     be used to fill the `ligID' fields.
+
+     The properties for all replacement glyphs are taken
+     from the glyph at position `buffer->in_pos'.
+
+     The cluster value for the glyph at position buffer->in_pos is used
+     for all replacement glyphs */
+  FT_Error
+  otl_buffer_add_output_glyphs( OTL_Buffer buffer,
+                               FT_UShort  num_in,
+                               FT_UShort  num_out,
+                               FT_UShort *glyph_data,
+                               FT_UShort  component,
+                               FT_UShort  ligID )
+  {
+    FT_Error  error;
+    FT_UShort i;
+    FT_UInt properties;
+    FT_UInt cluster;
+
+    error = otl_buffer_ensure( buffer, buffer->out_pos + num_out );
+    if ( error != FT_Err_Ok )
+      return error;
+
+    properties = buffer->in_string[buffer->in_pos].properties;
+    cluster = buffer->in_string[buffer->in_pos].cluster;
+    if ( component == 0xFFFF )
+      component = buffer->in_string[buffer->in_pos].component;
+    if ( ligID == 0xFFFF )
+      ligID = buffer->in_string[buffer->in_pos].ligID;
+
+    for ( i = 0; i < num_out; i++ )
+    {
+      OTL_GlyphItem item = &buffer->out_string[buffer->out_pos + i];
+
+      item->gindex = glyph_data[i];
+      item->properties = properties;
+      item->cluster = cluster;
+      item->component = component;
+      item->ligID = ligID;
+    }
+
+    buffer->in_pos  += num_in;
+    buffer->out_pos += num_out;
+
+    buffer->out_length = buffer->out_pos;
+
+    return FT_Err_Ok;
+  }
+
+  FT_Error
+  otl_buffer_add_output_glyph( OTL_Buffer buffer,
+                              FT_UInt    glyph_index,
+                              FT_UShort  component,
+                              FT_UShort  ligID )
+  {
+    FT_UShort glyph_data =  glyph_index;
+
+    return otl_buffer_add_output_glyphs ( buffer, 1, 1,
+                                         &glyph_data, component, ligID );
+  }
+
+  FT_UShort
+  otl_buffer_allocate_ligid( OTL_Buffer buffer )
+  {
+    return buffer->max_ligID++;
+  }
diff --git a/src/otlbuffer.h b/src/otlbuffer.h
new file mode 100644 (file)
index 0000000..426b447
--- /dev/null
@@ -0,0 +1,97 @@
+/* otlbuffer.h: Buffer of glyphs for substitution/positioning
+ *
+ * Copyrigh 2004 Red Hat Software
+ *
+ * Portions Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+  typedef struct OTL_GlyphItemRec_ {
+    FT_UInt     gindex;
+    FT_UInt     properties;
+    FT_UInt     cluster;
+    FT_UShort   component;
+    FT_UShort   ligID;
+  } OTL_GlyphItemRec, *OTL_GlyphItem;
+
+  typedef struct OTL_PositionRec_ {
+    FT_Pos     x_pos;
+    FT_Pos     y_pos;
+    FT_Pos     x_advance;
+    FT_Pos     y_advance;
+    FT_UShort  back;            /* number of glyphs to go back
+                                   for drawing current glyph   */
+    FT_Bool    new_advance;     /* if set, the advance width values are
+                                   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                     */
+  } OTL_PositionRec, *OTL_Position;
+
+
+  typedef struct OTL_BufferRec_{ 
+    FT_Memory   memory;
+    FT_ULong    allocated;
+
+    FT_ULong    in_length;
+    FT_ULong    out_length;
+    FT_ULong    in_pos;
+    FT_ULong    out_pos;
+    
+    OTL_GlyphItem  in_string;
+    OTL_GlyphItem  out_string;
+    OTL_Position   positions;
+    FT_UShort      max_ligID;
+  } OTL_BufferRec, *OTL_Buffer;
+
+  FT_Error
+  otl_buffer_new( FT_Memory   memory,
+                 OTL_Buffer *buffer );
+
+  FT_Error
+  otl_buffer_swap( OTL_Buffer buffer );
+
+  FT_Error
+  otl_buffer_free( OTL_Buffer buffer );
+
+  FT_Error
+  otl_buffer_clear( OTL_Buffer buffer );
+
+  FT_Error
+  otl_buffer_add_glyph( OTL_Buffer buffer,
+                       FT_UInt    glyph_index,
+                       FT_UInt    properties,
+                       FT_UInt    cluster );
+
+  FT_Error
+  otl_buffer_add_output_glyphs( OTL_Buffer buffer,
+                               FT_UShort  num_in,
+                               FT_UShort  num_out,
+                               FT_UShort *glyph_data,
+                               FT_UShort  component,
+                               FT_UShort  ligID );
+
+  FT_Error
+  otl_buffer_add_output_glyph ( OTL_Buffer buffer,
+                               FT_UInt    glyph_index,
+                               FT_UShort  component,
+                               FT_UShort  ligID );
+
+  FT_UShort
+  otl_buffer_allocate_ligid( OTL_Buffer buffer );
+
+G_END_DECLS
index b70f911..e9a81c9 100644 (file)
@@ -145,6 +145,7 @@ add_features (TTO_GSUB gsub)
   maybe_add_feature (gsub, script_index, FT_MAKE_TAG ('l', 'i', 'g', 'a'), L);
 }
 
+#if 0
 void 
 dump_string (TTO_GSUB_String *str)
 {
@@ -203,6 +204,7 @@ try_string (FT_Library library,
   if ((error = TT_GSUB_String_Done (out_str)))
     croak ("TT_GSUB_String_New", error);
 }
+#endif
 
 int 
 main (int argc, char **argv)
diff --git a/src/pango-ot-buffer.c b/src/pango-ot-buffer.c
new file mode 100644 (file)
index 0000000..9b54866
--- /dev/null
@@ -0,0 +1,265 @@
+/* Pango
+ * pango-ot-buffer.c: Buffer of glyphs for shaping/positioning
+ *
+ * Copyright (C) 2004 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "pango-ot-private.h"
+
+#define PANGO_SCALE_26_6 (PANGO_SCALE / (1<<6))
+#define PANGO_UNITS_26_6(d) (PANGO_SCALE_26_6 * (d))
+
+PangoOTBuffer *
+pango_ot_buffer_new (PangoFcFont *font)
+{
+  /* We lock the font here immediately for the silly reason
+   * of getting the FT_Memory; otherwise we'd have to
+   * add a new operation to PangoFcFontmap; callers will
+   * probably already have the font locked, however,
+   * so there is little performance penalty.
+   */
+  PangoOTBuffer *buffer = g_new (PangoOTBuffer, 1);
+  FT_Face face = pango_fc_font_lock_face (font);
+
+  if (otl_buffer_new (face->memory, &buffer->buffer) != FT_Err_Ok)
+    g_error ("Allocation of OTLBuffer failed");
+
+  buffer->font = g_object_ref (font);
+  buffer->applied_gpos = FALSE;
+  buffer->rtl = FALSE;
+
+  pango_fc_font_unlock_face (font);
+
+  return buffer;
+}
+
+void
+pango_ot_buffer_destroy (PangoOTBuffer *buffer)
+{
+  otl_buffer_free (buffer->buffer);
+  g_object_unref (buffer->font);
+  g_free (buffer);
+}
+
+void
+pango_ot_buffer_clear (PangoOTBuffer *buffer)
+{
+  otl_buffer_clear (buffer->buffer);
+  buffer->applied_gpos = FALSE;
+}
+
+void
+pango_ot_buffer_add_glyph (PangoOTBuffer *buffer,
+                          guint          glyph_index,
+                          guint          properties,
+                          guint          cluster)
+{
+  otl_buffer_add_glyph (buffer->buffer,
+                       glyph_index, properties, cluster);
+
+}
+
+void
+pango_ot_buffer_set_rtl (PangoOTBuffer *buffer,
+                        gboolean       rtl)
+{
+  rtl = rtl != FALSE;
+  buffer->rtl = rtl;
+}
+
+void
+pango_ot_buffer_get_glyphs (PangoOTBuffer  *buffer,
+                           PangoOTGlyph  **glyphs,
+                           int            *n_glyphs)
+{
+  if (glyphs)
+    *glyphs = (PangoOTGlyph *)buffer->buffer->in_string;
+
+  if (n_glyphs)
+    *n_glyphs = buffer->buffer->in_length;
+}
+
+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 (PangoGlyphString *glyphs,
+               OTL_Position      positions)
+{
+  int i;
+           
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      FT_Pos x_pos = positions[i].x_pos;
+      FT_Pos y_pos = positions[i].y_pos;
+      int back = i;
+      int j;
+      
+      while (positions[back].back != 0)
+       {
+         back  -= positions[back].back;
+         x_pos += positions[back].x_pos;
+         y_pos += positions[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 (positions[i].new_advance)
+       glyphs->glyphs[i].geometry.width  = PANGO_UNITS_26_6(positions[i].x_advance);
+      else
+       glyphs->glyphs[i].geometry.width += PANGO_UNITS_26_6(positions[i].x_advance);
+    }
+}
+
+static void
+apply_gpos_rtl (PangoGlyphString *glyphs,
+               OTL_Position      positions)
+{
+  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 = positions[i_rev].x_pos;
+      FT_Pos y_pos = positions[i_rev].y_pos;
+      int j;
+
+      while (positions[back_rev].back != 0)
+       {
+         back_rev -= positions[back_rev].back;
+         x_pos += positions[back_rev].x_pos;
+         y_pos += positions[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 (positions[i_rev].new_advance)
+       glyphs->glyphs[i].geometry.width  = PANGO_UNITS_26_6(positions[i_rev].x_advance);
+      else
+       glyphs->glyphs[i].geometry.width += PANGO_UNITS_26_6(positions[i_rev].x_advance);
+    }
+}
+
+void
+pango_ot_buffer_output (PangoOTBuffer    *buffer,
+                       PangoGlyphString *glyphs)
+{
+  FT_Face face;
+  PangoOTInfo *info;
+  TTO_GDEF gdef = NULL;
+  int i;
+  int last_cluster;
+
+  face = pango_fc_font_lock_face (buffer->font);
+  g_assert (face);
+  
+  /* Copy glyphs into output glyph string */
+  pango_glyph_string_set_size (glyphs, buffer->buffer->in_length);
+
+  last_cluster = -1;
+  for (i = 0; i < buffer->buffer->in_length; i++)
+    {
+      OTL_GlyphItem item = &buffer->buffer->in_string[i];
+      
+      glyphs->glyphs[i].glyph = item->gindex;
+
+      glyphs->log_clusters[i] = item->cluster;
+      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];
+    }
+
+  info = pango_ot_info_get (face);
+  gdef = pango_ot_info_get_gdef (info);
+  
+  /* 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 ((PangoFont *)buffer->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 (buffer->rtl)
+    {
+      /* Swap all glyphs */
+      swap_range (glyphs, 0, glyphs->num_glyphs);
+    }
+
+  if (buffer->applied_gpos)
+    {
+      if (buffer->rtl)
+       apply_gpos_rtl (glyphs, buffer->buffer->positions);
+      else
+       apply_gpos_ltr (glyphs, buffer->buffer->positions);
+    }
+
+  pango_fc_font_unlock_face (buffer->font);
+}
index 578c95d..a6baa7f 100644 (file)
@@ -162,17 +162,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;
     }
 }
 
@@ -225,9 +239,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 5c27f3a..9d8f5d0 100644 (file)
@@ -80,6 +80,14 @@ struct _PangoOTRulesetClass
   GObjectClass parent_class;
 };
 
+struct _PangoOTBuffer
+{
+  OTL_Buffer buffer;
+  PangoFcFont *font;
+  gboolean rtl;
+  gboolean applied_gpos;
+};
+
 GType pango_ot_info_get_type (void);
 
 TTO_GDEF pango_ot_info_get_gdef (PangoOTInfo *info);
index bf297f1..47af334 100644 (file)
@@ -24,9 +24,6 @@
 
 #include FT_INTERNAL_MEMORY_H  /* For FT_Free() */
 
-#define PANGO_SCALE_26_6 (PANGO_SCALE / (1<<6))
-#define PANGO_UNITS_26_6(d) (PANGO_SCALE_26_6 * (d))
-
 typedef struct _PangoOTRule PangoOTRule;
 
 struct _PangoOTRule 
@@ -139,34 +136,15 @@ pango_ot_ruleset_add_feature (PangoOTRuleset   *ruleset,
   g_array_append_val (ruleset->rules, tmp_rule);
 }
 
-/**
- * pango_ot_ruleset_shape:
- * @ruleset: a #PangoOTRuleset.
- * @glyphs: a pointer to a #PangoGlyphString.
- * @properties: an array containing one #gulong bitfield for each glyph,
- *   which gives the glyph's properties: If a certain bit is set for a glyph, 
- *   the feature which has the same bit set in its property value is applied.
- *
- * Shapes a string of glyphs with the given properties according to @ruleset.
- **/
 void
-pango_ot_ruleset_shape (PangoOTRuleset   *ruleset,
-                       PangoGlyphString *glyphs,
-                       gulong           *properties)
+pango_ot_ruleset_substitute  (PangoOTRuleset   *ruleset,
+                             PangoOTBuffer    *buffer)
 {
   int i;
-  int last_cluster;
-  int result;
   
   TTO_GSUB gsub = NULL;
-  TTO_GPOS gpos = NULL;
   
-  TTO_GSUB_String *in_string = NULL;
-  TTO_GSUB_String *out_string = NULL;
-  TTO_GSUB_String *result_string = NULL;
-
   gboolean need_gsub = FALSE;
-  gboolean need_gpos = FALSE;
 
   g_return_if_fail (PANGO_OT_IS_RULESET (ruleset));
 
@@ -176,26 +154,17 @@ pango_ot_ruleset_shape (PangoOTRuleset   *ruleset,
 
       if (rule->table_type == PANGO_OT_TABLE_GSUB)
        need_gsub = TRUE;
-      else 
-       need_gpos = TRUE;
     }
 
   if (need_gsub)
     {
+
       gsub = pango_ot_info_get_gsub (ruleset->info);
 
       if (gsub)
        TT_GSUB_Clear_Features (gsub);
     }
 
-  if (need_gpos)
-    {
-      gpos = pango_ot_info_get_gpos (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);
@@ -205,98 +174,59 @@ pango_ot_ruleset_shape (PangoOTRuleset   *ruleset,
          if (gsub)
            TT_GSUB_Add_Feature (gsub, rule->feature_index, rule->property_bit);
        }
-      else
-       {
-         if (gpos)
-           TT_GPOS_Add_Feature (gpos, rule->feature_index, rule->property_bit);
-       }
     }
 
-  if (!gsub && !gpos)
+  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);
+  TT_GSUB_Apply_String (gsub, buffer->buffer);
+}
 
-  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;
+void
+pango_ot_ruleset_position (PangoOTRuleset   *ruleset,
+                          PangoOTBuffer    *buffer)
+{
+  int i;
+  
+  TTO_GPOS gpos = NULL;
   
-  if (gsub)
+  gboolean need_gpos = FALSE;
+
+  g_return_if_fail (PANGO_OT_IS_RULESET (ruleset));
+
+  for (i = 0; i < ruleset->rules->len; i++)
     {
-      result = TT_GSUB_String_New (ruleset->info->face->memory,
-                                   &out_string);
-      g_assert (result == FT_Err_Ok);
-      result_string = out_string;
+      PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
 
-      TT_GSUB_Apply_String (gsub, in_string, out_string);
+      if (rule->table_type == PANGO_OT_TABLE_GPOS)
+       need_gpos = TRUE;
     }
-  else
-    result_string = in_string;
+
+  if (need_gpos)
+    gpos = pango_ot_info_get_gpos (ruleset->info);
 
   if (gpos)
     {
-      TTO_GPOS_Data *outgpos = NULL;
+      TT_GPOS_Clear_Features (gpos);
 
-      if (!TT_GPOS_Apply_String (ruleset->info->face, gpos, 0, result_string, &outgpos,
-                                FALSE /* enable device-dependant values */,
-                                FALSE /* Even though this might be r2l text, RTL is handled elsewhere */))
+      for (i = 0; i < ruleset->rules->len; i++)
        {
-         for (i = 0; i < result_string->length; 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);
-           }
-
-         FT_Free(gpos->memory, (void *)outgpos);
+         PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+         
+         if (rule->table_type == PANGO_OT_TABLE_GPOS)
+           TT_GPOS_Add_Feature (gpos, rule->feature_index, rule->property_bit);
        }
     }
 
-  pango_glyph_string_set_size (glyphs, result_string->length);
-
-  last_cluster = -1;
-  for (i = 0; i < result_string->length; i++)
+  /* Apply GPOS rules */
+  if (gpos)
     {
-      glyphs->glyphs[i].glyph = result_string->string[i];
-
-      glyphs->log_clusters[i] = result_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 (TT_GPOS_Apply_String (ruleset->info->face, gpos, 0, buffer->buffer,
+                               FALSE /* enable device-dependant values */,
+                               buffer->rtl) == FT_Err_Ok)
+       {
+         buffer->applied_gpos = TRUE;
+       }
     }
-
-  if (in_string)
-    TT_GSUB_String_Done (in_string);
-  if (out_string)
-    TT_GSUB_String_Done (out_string);
 }
+