diff options
| author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-07-10 15:17:53 -0500 | 
|---|---|---|
| committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2011-07-10 15:17:53 -0500 | 
| commit | dda8474928bd7276e1fad8fb7a601e7c83ff2bc2 (patch) | |
| tree | 7f83910598b33b12730035f086df20b5a53ab99c /tqtinterface/qt4/include/private/qt4_harfbuzz/src/harfbuzz-gsub.c | |
| parent | 6260b6178868c03aab1644bf93b0ef043654bdb0 (diff) | |
| download | experimental-dda8474928bd7276e1fad8fb7a601e7c83ff2bc2.tar.gz experimental-dda8474928bd7276e1fad8fb7a601e7c83ff2bc2.zip | |
Added TQt4 HEAD
Diffstat (limited to 'tqtinterface/qt4/include/private/qt4_harfbuzz/src/harfbuzz-gsub.c')
| -rw-r--r-- | tqtinterface/qt4/include/private/qt4_harfbuzz/src/harfbuzz-gsub.c | 4329 | 
1 files changed, 4329 insertions, 0 deletions
| diff --git a/tqtinterface/qt4/include/private/qt4_harfbuzz/src/harfbuzz-gsub.c b/tqtinterface/qt4/include/private/qt4_harfbuzz/src/harfbuzz-gsub.c new file mode 100644 index 0000000..21fec51 --- /dev/null +++ b/tqtinterface/qt4/include/private/qt4_harfbuzz/src/harfbuzz-gsub.c @@ -0,0 +1,4329 @@ +/* + * Copyright (C) 1998-2004  David Turner and Werner Lemberg + * Copyright (C) 2006  Behdad Esfahbod + * Copyright (C) 2007  Red Hat, Inc. + * + * This is part of HarfBuzz, an OpenType Layout engine library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#include "harfbuzz-impl.h" +#include "harfbuzz-gsub-private.h" +#include "harfbuzz-open-private.h" +#include "harfbuzz-gdef-private.h" + +static HB_Error  GSUB_Do_Glyph_Lookup( HB_GSUBHeader*   gsub, +				       HB_UShort         lookup_index, +				       HB_Buffer        buffer, +				       HB_UShort         context_length, +				       int               nesting_level ); + + + +/********************** + * Auxiliary functions + **********************/ + + + +HB_Error  HB_Load_GSUB_Table( HB_Stream stream, +			      HB_GSUBHeader** retptr, +			      HB_GDEFHeader*  gdef, +                              HB_Stream       gdefStream ) +{ +  HB_Error         error; +  HB_UInt         cur_offset, new_offset, base_offset; + +  HB_GSUBHeader*  gsub; + +  if ( !retptr ) +    return ERR(HB_Err_Invalid_Argument); + +  if ( GOTO_Table( TTAG_GSUB ) ) +    return error; + +  base_offset = FILE_Pos(); + +  if ( ALLOC ( gsub, sizeof( *gsub ) ) )  +      return error; +   + +  /* skip version */ + +  if ( FILE_Seek( base_offset + 4L ) || +       ACCESS_Frame( 2L ) ) +    goto Fail4; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_ScriptList( &gsub->ScriptList, +				  stream ) ) != HB_Err_Ok ) +    goto Fail4; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail3; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_FeatureList( &gsub->FeatureList, +				   stream ) ) != HB_Err_Ok ) +    goto Fail3; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_LookupList( &gsub->LookupList, +				  stream, HB_Type_GSUB ) ) != HB_Err_Ok ) +    goto Fail2; + +  gsub->gdef = gdef;      /* can be NULL */ + +  if ( ( error =  _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( gdef, gdefStream, +								     gsub->LookupList.Lookup, +								     gsub->LookupList.LookupCount ) ) ) +    goto Fail1; + +  *retptr = gsub; + +  return HB_Err_Ok; + +Fail1: +  _HB_OPEN_Free_LookupList( &gsub->LookupList, HB_Type_GSUB ); + +Fail2: +  _HB_OPEN_Free_FeatureList( &gsub->FeatureList ); + +Fail3: +  _HB_OPEN_Free_ScriptList( &gsub->ScriptList ); + +Fail4: +  FREE ( gsub ); + + +  return error; +} + + +HB_Error   HB_Done_GSUB_Table( HB_GSUBHeader* gsub ) +{ +  _HB_OPEN_Free_LookupList( &gsub->LookupList, HB_Type_GSUB ); +  _HB_OPEN_Free_FeatureList( &gsub->FeatureList ); +  _HB_OPEN_Free_ScriptList( &gsub->ScriptList ); + +  FREE( gsub ); + +  return HB_Err_Ok; +} + +/***************************** + * SubTable related functions + *****************************/ + + +/* LookupType 1 */ + +/* SingleSubstFormat1 */ +/* SingleSubstFormat2 */ + +static HB_Error  Load_SingleSubst( HB_GSUB_SubTable* st, +				   HB_Stream         stream ) +{ +  HB_Error error; +  HB_SingleSubst*  ss = &st->single; + +  HB_UShort n, count; +  HB_UInt cur_offset, new_offset, base_offset; + +  HB_UShort*  s; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  ss->SubstFormat = GET_UShort(); +  new_offset      = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &ss->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  switch ( ss->SubstFormat ) +  { +  case 1: +    if ( ACCESS_Frame( 2L ) ) +      goto Fail2; + +    ss->ssf.ssf1.DeltaGlyphID = GET_UShort(); + +    FORGET_Frame(); + +    break; + +  case 2: +    if ( ACCESS_Frame( 2L ) ) +      goto Fail2; + +    count = ss->ssf.ssf2.GlyphCount = GET_UShort(); + +    FORGET_Frame(); + +    ss->ssf.ssf2.Substitute = NULL; + +    if ( ALLOC_ARRAY( ss->ssf.ssf2.Substitute, count, HB_UShort ) ) +      goto Fail2; + +    s = ss->ssf.ssf2.Substitute; + +    if ( ACCESS_Frame( count * 2L ) ) +      goto Fail1; + +    for ( n = 0; n < count; n++ ) +      s[n] = GET_UShort(); + +    FORGET_Frame(); + +    break; + +  default: +    return ERR(HB_Err_Invalid_SubTable_Format); +  } + +  return HB_Err_Ok; + +Fail1: +  FREE( s ); + +Fail2: +  _HB_OPEN_Free_Coverage( &ss->Coverage ); +  return error; +} + + +static void  Free_SingleSubst( HB_GSUB_SubTable* st ) +{ +  HB_SingleSubst*  ss = &st->single; + +  switch ( ss->SubstFormat ) +  { +  case 1: +    break; + +  case 2: +    FREE( ss->ssf.ssf2.Substitute ); +    break; + +  default: +    break; +  } + +  _HB_OPEN_Free_Coverage( &ss->Coverage ); +} + + +static HB_Error  Lookup_SingleSubst( HB_GSUBHeader*   gsub, +				     HB_GSUB_SubTable* st, +				     HB_Buffer        buffer, +				     HB_UShort         flags, +				     HB_UShort         context_length, +				     int               nesting_level ) +{ +  HB_UShort index, value, property; +  HB_Error  error; +  HB_SingleSubst*  ss = &st->single; +  HB_GDEFHeader*   gdef = gsub->gdef; + +  HB_UNUSED(nesting_level); + +  if ( context_length != 0xFFFF && context_length < 1 ) +    return HB_Err_Not_Covered; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  error = _HB_OPEN_Coverage_Index( &ss->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  switch ( ss->SubstFormat ) +  { +  case 1: +    value = ( IN_CURGLYPH() + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF; +    if ( REPLACE_Glyph( buffer, value, nesting_level ) ) +      return error; +    break; + +  case 2: +    if ( index >= ss->ssf.ssf2.GlyphCount ) +      return ERR(HB_Err_Invalid_SubTable); +    value = ss->ssf.ssf2.Substitute[index]; +    if ( REPLACE_Glyph( buffer, value, nesting_level ) ) +      return error; +    break; + +  default: +    return ERR(HB_Err_Invalid_SubTable); +  } + +  if ( gdef && gdef->NewGlyphClasses ) +  { +    /* we inherit the old glyph class to the substituted glyph */ + +    error = _HB_GDEF_Add_Glyph_Property( gdef, value, property ); +    if ( error && error != HB_Err_Not_Covered ) +      return error; +  } + +  return HB_Err_Ok; +} + + +/* LookupType 2 */ + +/* Sequence */ + +static HB_Error  Load_Sequence( HB_Sequence*  s, +				HB_Stream      stream ) +{ +  HB_Error error; + +  HB_UShort n, count; +  HB_UShort*  sub; + + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = s->GlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  s->Substitute = NULL; + +  if ( count ) +  { +    if ( ALLOC_ARRAY( s->Substitute, count, HB_UShort ) ) +      return error; + +    sub = s->Substitute; + +    if ( ACCESS_Frame( count * 2L ) ) +    { +      FREE( sub ); +      return error; +    } + +    for ( n = 0; n < count; n++ ) +      sub[n] = GET_UShort(); + +    FORGET_Frame(); +  } + +  return HB_Err_Ok; +} + + +static void  Free_Sequence( HB_Sequence*  s ) +{ +  FREE( s->Substitute ); +} + + +/* MultipleSubstFormat1 */ + +static HB_Error  Load_MultipleSubst( HB_GSUB_SubTable* st, +				     HB_Stream         stream ) +{ +  HB_Error error; +  HB_MultipleSubst*  ms = &st->multiple; + +  HB_UShort      n = 0, m, count; +  HB_UInt       cur_offset, new_offset, base_offset; + +  HB_Sequence*  s; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  ms->SubstFormat = GET_UShort();             /* should be 1 */ +  new_offset      = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &ms->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  count = ms->SequenceCount = GET_UShort(); + +  FORGET_Frame(); + +  ms->Sequence = NULL; + +  if ( ALLOC_ARRAY( ms->Sequence, count, HB_Sequence ) ) +    goto Fail2; + +  s = ms->Sequence; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_Sequence( &s[n], stream ) ) != HB_Err_Ok ) +      goto Fail1; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_Sequence( &s[m] ); + +  FREE( s ); + +Fail2: +  _HB_OPEN_Free_Coverage( &ms->Coverage ); +  return error; +} + + +static void  Free_MultipleSubst( HB_GSUB_SubTable* st ) +{ +  HB_UShort      n, count; +  HB_MultipleSubst*  ms = &st->multiple; + +  HB_Sequence*  s; + + +  if ( ms->Sequence ) +  { +    count = ms->SequenceCount; +    s     = ms->Sequence; + +    for ( n = 0; n < count; n++ ) +      Free_Sequence( &s[n] ); + +    FREE( s ); +  } + +  _HB_OPEN_Free_Coverage( &ms->Coverage ); +} + + +static HB_Error  Lookup_MultipleSubst( HB_GSUBHeader*    gsub, +				       HB_GSUB_SubTable* st, +				       HB_Buffer         buffer, +				       HB_UShort          flags, +				       HB_UShort          context_length, +				       int                nesting_level ) +{ +  HB_Error  error; +  HB_UShort index, property, n, count; +  HB_UShort*s; +  HB_MultipleSubst*  ms = &st->multiple; +  HB_GDEFHeader*     gdef = gsub->gdef; + +  HB_UNUSED(nesting_level); + +  if ( context_length != 0xFFFF && context_length < 1 ) +    return HB_Err_Not_Covered; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  error = _HB_OPEN_Coverage_Index( &ms->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  if ( index >= ms->SequenceCount ) +    return ERR(HB_Err_Invalid_SubTable); + +  count = ms->Sequence[index].GlyphCount; +  s     = ms->Sequence[index].Substitute; + +  if ( ADD_String( buffer, 1, count, s, 0xFFFF, 0xFFFF ) ) +    return error; + +  if ( gdef && gdef->NewGlyphClasses ) +  { +    /* this is a guess only ... */ + +    if ( property == HB_GDEF_LIGATURE ) +      property = HB_GDEF_BASE_GLYPH; + +    for ( n = 0; n < count; n++ ) +    { +      error = _HB_GDEF_Add_Glyph_Property( gdef, s[n], property ); +      if ( error && error != HB_Err_Not_Covered ) +	return error; +    } +  } + +  return HB_Err_Ok; +} + + +/* LookupType 3 */ + +/* AlternateSet */ + +static HB_Error  Load_AlternateSet( HB_AlternateSet*  as, +				    HB_Stream          stream ) +{ +  HB_Error error; + +  HB_UShort n, count; +  HB_UShort*  a; + + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = as->GlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  as->Alternate = NULL; + +  if ( ALLOC_ARRAY( as->Alternate, count, HB_UShort ) ) +    return error; + +  a = as->Alternate; + +  if ( ACCESS_Frame( count * 2L ) ) +  { +    FREE( a ); +    return error; +  } + +  for ( n = 0; n < count; n++ ) +    a[n] = GET_UShort(); + +  FORGET_Frame(); + +  return HB_Err_Ok; +} + + +static void  Free_AlternateSet( HB_AlternateSet*  as ) +{ +  FREE( as->Alternate ); +} + + +/* AlternateSubstFormat1 */ + +static HB_Error  Load_AlternateSubst( HB_GSUB_SubTable* st, +				      HB_Stream         stream ) +{ +  HB_Error error; +  HB_AlternateSubst* as = &st->alternate; + +  HB_UShort          n = 0, m, count; +  HB_UInt           cur_offset, new_offset, base_offset; + +  HB_AlternateSet*  aset; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  as->SubstFormat = GET_UShort();             /* should be 1 */ +  new_offset      = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &as->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  count = as->AlternateSetCount = GET_UShort(); + +  FORGET_Frame(); + +  as->AlternateSet = NULL; + +  if ( ALLOC_ARRAY( as->AlternateSet, count, HB_AlternateSet ) ) +    goto Fail2; + +  aset = as->AlternateSet; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_AlternateSet( &aset[n], stream ) ) != HB_Err_Ok ) +      goto Fail1; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_AlternateSet( &aset[m] ); + +  FREE( aset ); + +Fail2: +  _HB_OPEN_Free_Coverage( &as->Coverage ); +  return error; +} + + +static void  Free_AlternateSubst( HB_GSUB_SubTable* st ) +{ +  HB_UShort          n, count; +  HB_AlternateSubst* as = &st->alternate; + +  HB_AlternateSet*  aset; + + +  if ( as->AlternateSet ) +  { +    count = as->AlternateSetCount; +    aset  = as->AlternateSet; + +    for ( n = 0; n < count; n++ ) +      Free_AlternateSet( &aset[n] ); + +    FREE( aset ); +  } + +  _HB_OPEN_Free_Coverage( &as->Coverage ); +} + + +static HB_Error  Lookup_AlternateSubst( HB_GSUBHeader*    gsub, +					HB_GSUB_SubTable* st, +					HB_Buffer         buffer, +					HB_UShort          flags, +					HB_UShort          context_length, +					int                nesting_level ) +{ +  HB_Error          error; +  HB_UShort         index, value, alt_index, property; +  HB_AlternateSubst* as = &st->alternate; +  HB_GDEFHeader*     gdef = gsub->gdef; +  HB_AlternateSet  aset; + +  HB_UNUSED(nesting_level); + +  if ( context_length != 0xFFFF && context_length < 1 ) +    return HB_Err_Not_Covered; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  error = _HB_OPEN_Coverage_Index( &as->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  aset = as->AlternateSet[index]; + +  /* we use a user-defined callback function to get the alternate index */ + +  if ( gsub->altfunc ) +    alt_index = (gsub->altfunc)( buffer->out_pos, IN_CURGLYPH(), +				 aset.GlyphCount, aset.Alternate, +				 gsub->data ); +  else +    alt_index = 0; + +  value = aset.Alternate[alt_index]; +  if ( REPLACE_Glyph( buffer, value, nesting_level ) ) +    return error; + +  if ( gdef && gdef->NewGlyphClasses ) +  { +    /* we inherit the old glyph class to the substituted glyph */ + +    error = _HB_GDEF_Add_Glyph_Property( gdef, value, property ); +    if ( error && error != HB_Err_Not_Covered ) +      return error; +  } + +  return HB_Err_Ok; +} + + +/* LookupType 4 */ + +/* Ligature */ + +static HB_Error  Load_Ligature( HB_Ligature*  l, +				HB_Stream      stream ) +{ +  HB_Error error; + +  HB_UShort n, count; +  HB_UShort*  c; + + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  l->LigGlyph       = GET_UShort(); +  l->ComponentCount = GET_UShort(); + +  FORGET_Frame(); + +  l->Component = NULL; + +  count = l->ComponentCount - 1;      /* only ComponentCount - 1 elements */ + +  if ( ALLOC_ARRAY( l->Component, count, HB_UShort ) ) +    return error; + +  c = l->Component; + +  if ( ACCESS_Frame( count * 2L ) ) +  { +    FREE( c ); +    return error; +  } + +  for ( n = 0; n < count; n++ ) +    c[n] = GET_UShort(); + +  FORGET_Frame(); + +  return HB_Err_Ok; +} + + +static void  Free_Ligature( HB_Ligature*  l ) +{ +  FREE( l->Component ); +} + + +/* LigatureSet */ + +static HB_Error  Load_LigatureSet( HB_LigatureSet*  ls, +				   HB_Stream         stream ) +{ +  HB_Error error; + +  HB_UShort      n = 0, m, count; +  HB_UInt       cur_offset, new_offset, base_offset; + +  HB_Ligature*  l; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = ls->LigatureCount = GET_UShort(); + +  FORGET_Frame(); + +  ls->Ligature = NULL; + +  if ( ALLOC_ARRAY( ls->Ligature, count, HB_Ligature ) ) +    return error; + +  l = ls->Ligature; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_Ligature( &l[n], stream ) ) != HB_Err_Ok ) +      goto Fail; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail: +  for ( m = 0; m < n; m++ ) +    Free_Ligature( &l[m] ); + +  FREE( l ); +  return error; +} + + +static void  Free_LigatureSet( HB_LigatureSet*  ls ) +{ +  HB_UShort      n, count; + +  HB_Ligature*  l; + + +  if ( ls->Ligature ) +  { +    count = ls->LigatureCount; +    l     = ls->Ligature; + +    for ( n = 0; n < count; n++ ) +      Free_Ligature( &l[n] ); + +    FREE( l ); +  } +} + + +/* LigatureSubstFormat1 */ + +static HB_Error  Load_LigatureSubst( HB_GSUB_SubTable* st, +				     HB_Stream         stream ) +{ +  HB_Error error; +  HB_LigatureSubst*  ls = &st->ligature; + +  HB_UShort         n = 0, m, count; +  HB_UInt          cur_offset, new_offset, base_offset; + +  HB_LigatureSet*  lset; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  ls->SubstFormat = GET_UShort();             /* should be 1 */ +  new_offset      = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &ls->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  count = ls->LigatureSetCount = GET_UShort(); + +  FORGET_Frame(); + +  ls->LigatureSet = NULL; + +  if ( ALLOC_ARRAY( ls->LigatureSet, count, HB_LigatureSet ) ) +    goto Fail2; + +  lset = ls->LigatureSet; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_LigatureSet( &lset[n], stream ) ) != HB_Err_Ok ) +      goto Fail1; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_LigatureSet( &lset[m] ); + +  FREE( lset ); + +Fail2: +  _HB_OPEN_Free_Coverage( &ls->Coverage ); +  return error; +} + + +static void  Free_LigatureSubst( HB_GSUB_SubTable* st ) +{ +  HB_UShort         n, count; +  HB_LigatureSubst*  ls = &st->ligature; + +  HB_LigatureSet*  lset; + + +  if ( ls->LigatureSet ) +  { +    count = ls->LigatureSetCount; +    lset  = ls->LigatureSet; + +    for ( n = 0; n < count; n++ ) +      Free_LigatureSet( &lset[n] ); + +    FREE( lset ); +  } + +  _HB_OPEN_Free_Coverage( &ls->Coverage ); +} + + +static HB_Error  Lookup_LigatureSubst( HB_GSUBHeader*    gsub, +				       HB_GSUB_SubTable* st, +				       HB_Buffer         buffer, +				       HB_UShort          flags, +				       HB_UShort          context_length, +				       int                nesting_level ) +{ +  HB_UShort      index, property; +  HB_Error       error; +  HB_UShort      numlig, i, j, is_mark, first_is_mark = FALSE; +  HB_UShort*     c; +  HB_LigatureSubst*  ls = &st->ligature; +  HB_GDEFHeader*     gdef = gsub->gdef; + +  HB_Ligature*  lig; + +  HB_UNUSED(nesting_level); + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  if ( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) +    first_is_mark = TRUE; + +  error = _HB_OPEN_Coverage_Index( &ls->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  if ( index >= ls->LigatureSetCount ) +     return ERR(HB_Err_Invalid_SubTable); + +  lig = ls->LigatureSet[index].Ligature; + +  for ( numlig = ls->LigatureSet[index].LigatureCount; +	numlig; +	numlig--, lig++ ) +  { +    if ( buffer->in_pos + lig->ComponentCount > buffer->in_length ) +      goto next_ligature;               /* Not enough glyphs in input */ + +    c    = lig->Component; + +    is_mark = first_is_mark; + +    if ( context_length != 0xFFFF && context_length < lig->ComponentCount ) +      break; + +    for ( i = 1, j = buffer->in_pos + 1; i < lig->ComponentCount; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  return error; + +	if ( j + lig->ComponentCount - i == (HB_Int)buffer->in_length ) +	  goto next_ligature; +	j++; +      } + +      if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) ) +	is_mark = FALSE; + +      if ( IN_GLYPH( j ) != c[i - 1] ) +	goto next_ligature; +    } + +    if ( gdef && gdef->NewGlyphClasses ) +    { +      /* this is just a guess ... */ + +      error = _HB_GDEF_Add_Glyph_Property( gdef, lig->LigGlyph, +				  is_mark ? HB_GDEF_MARK : HB_GDEF_LIGATURE ); +      if ( error && error != HB_Err_Not_Covered ) +	return error; +    } + +    if ( j == buffer->in_pos + i ) /* No input glyphs skipped */ +    { +      /* We don't use a new ligature ID if there are no skipped +	 glyphs and the ligature already has an ID.             */ + +      if ( IN_LIGID( buffer->in_pos ) ) +      { +	if ( ADD_String( buffer, i, 1, &lig->LigGlyph, +			0xFFFF, 0xFFFF ) ) +	  return error; +      } +      else +      { +	HB_UShort ligID = _hb_buffer_allocate_ligid( buffer ); +	if ( ADD_String( buffer, i, 1, &lig->LigGlyph, +			0xFFFF, ligID ) ) +	  return error; +      } +    } +    else +    { +      HB_UShort ligID = _hb_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 +	 `out' and assign component values to it.  We start with the +	 glyph after the first component.  Glyphs between component +	 i and i+1 belong to component i.  Together with the ligID +	 value it is later possible to check whether a specific +	 component value really belongs to a given ligature.         */ + +      for ( i = 0; i < lig->ComponentCount - 1; i++ ) +      { +	while ( CHECK_Property( gdef, IN_CURITEM(), +				flags, &property ) ) +	  if ( ADD_Glyph( buffer, IN_CURGLYPH(), i, ligID ) ) +	    return error; + +	(buffer->in_pos)++; +      } +    } + +    return HB_Err_Ok; + +  next_ligature: +    ; +  } + +  return HB_Err_Not_Covered; +} + + +/* Do the actual substitution for a context substitution (either format +   5 or 6).  This is only called after we've determined that the input +   matches the subrule.                                                 */ + +static HB_Error  Do_ContextSubst( HB_GSUBHeader*        gsub, +				  HB_UShort              GlyphCount, +				  HB_UShort              SubstCount, +				  HB_SubstLookupRecord* subst, +				  HB_Buffer             buffer, +				  int                    nesting_level ) +{ +  HB_Error  error; +  HB_UInt   i, old_pos; + + +  i = 0; + +  while ( i < GlyphCount ) +  { +    if ( SubstCount && i == subst->SequenceIndex ) +    { +      old_pos = buffer->in_pos; + +      /* Do a substitution */ + +      error = GSUB_Do_Glyph_Lookup( gsub, subst->LookupListIndex, buffer, +				    GlyphCount, nesting_level ); + +      subst++; +      SubstCount--; +      i += buffer->in_pos - old_pos; + +      if ( error == HB_Err_Not_Covered ) +      { +	if ( COPY_Glyph( buffer ) ) +	  return error; +	i++; +      } +      else if ( error ) +	return error; +    } +    else +    { +      /* No substitution for this index */ + +      if ( COPY_Glyph( buffer ) ) +	return error; +      i++; +    } +  } + +  return HB_Err_Ok; +} + + +/* LookupType 5 */ + +/* SubRule */ + +static HB_Error  Load_SubRule( HB_SubRule*  sr, +			       HB_Stream     stream ) +{ +  HB_Error error; + +  HB_UShort               n, count; +  HB_UShort*              i; + +  HB_SubstLookupRecord*  slr; + + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  sr->GlyphCount = GET_UShort(); +  sr->SubstCount = GET_UShort(); + +  FORGET_Frame(); + +  sr->Input = NULL; + +  count = sr->GlyphCount - 1;         /* only GlyphCount - 1 elements */ + +  if ( ALLOC_ARRAY( sr->Input, count, HB_UShort ) ) +    return error; + +  i = sr->Input; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail2; + +  for ( n = 0; n < count; n++ ) +    i[n] = GET_UShort(); + +  FORGET_Frame(); + +  sr->SubstLookupRecord = NULL; + +  count = sr->SubstCount; + +  if ( ALLOC_ARRAY( sr->SubstLookupRecord, count, HB_SubstLookupRecord ) ) +    goto Fail2; + +  slr = sr->SubstLookupRecord; + +  if ( ACCESS_Frame( count * 4L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +  { +    slr[n].SequenceIndex   = GET_UShort(); +    slr[n].LookupListIndex = GET_UShort(); +  } + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( slr ); + +Fail2: +  FREE( i ); +  return error; +} + + +static void  Free_SubRule( HB_SubRule*  sr ) +{ +  FREE( sr->SubstLookupRecord ); +  FREE( sr->Input ); +} + + +/* SubRuleSet */ + +static HB_Error  Load_SubRuleSet( HB_SubRuleSet*  srs, +				  HB_Stream        stream ) +{ +  HB_Error error; + +  HB_UShort     n = 0, m, count; +  HB_UInt      cur_offset, new_offset, base_offset; + +  HB_SubRule*  sr; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = srs->SubRuleCount = GET_UShort(); + +  FORGET_Frame(); + +  srs->SubRule = NULL; + +  if ( ALLOC_ARRAY( srs->SubRule, count, HB_SubRule ) ) +    return error; + +  sr = srs->SubRule; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_SubRule( &sr[n], stream ) ) != HB_Err_Ok ) +      goto Fail; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail: +  for ( m = 0; m < n; m++ ) +    Free_SubRule( &sr[m] ); + +  FREE( sr ); +  return error; +} + + +static void  Free_SubRuleSet( HB_SubRuleSet*  srs ) +{ +  HB_UShort     n, count; + +  HB_SubRule*  sr; + + +  if ( srs->SubRule ) +  { +    count = srs->SubRuleCount; +    sr    = srs->SubRule; + +    for ( n = 0; n < count; n++ ) +      Free_SubRule( &sr[n] ); + +    FREE( sr ); +  } +} + + +/* ContextSubstFormat1 */ + +static HB_Error  Load_ContextSubst1( HB_ContextSubstFormat1*  csf1, +				     HB_Stream                 stream ) +{ +  HB_Error error; + +  HB_UShort        n = 0, m, count; +  HB_UInt         cur_offset, new_offset, base_offset; + +  HB_SubRuleSet*  srs; + + +  base_offset = FILE_Pos() - 2L; + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &csf1->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  count = csf1->SubRuleSetCount = GET_UShort(); + +  FORGET_Frame(); + +  csf1->SubRuleSet = NULL; + +  if ( ALLOC_ARRAY( csf1->SubRuleSet, count, HB_SubRuleSet ) ) +    goto Fail2; + +  srs = csf1->SubRuleSet; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_SubRuleSet( &srs[n], stream ) ) != HB_Err_Ok ) +      goto Fail1; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_SubRuleSet( &srs[m] ); + +  FREE( srs ); + +Fail2: +  _HB_OPEN_Free_Coverage( &csf1->Coverage ); +  return error; +} + + +static void  Free_ContextSubst1( HB_ContextSubstFormat1* csf1 ) +{ +  HB_UShort        n, count; + +  HB_SubRuleSet*  srs; + + +  if ( csf1->SubRuleSet ) +  { +    count = csf1->SubRuleSetCount; +    srs   = csf1->SubRuleSet; + +    for ( n = 0; n < count; n++ ) +      Free_SubRuleSet( &srs[n] ); + +    FREE( srs ); +  } + +  _HB_OPEN_Free_Coverage( &csf1->Coverage ); +} + + +/* SubClassRule */ + +static HB_Error  Load_SubClassRule( HB_ContextSubstFormat2*  csf2, +				    HB_SubClassRule*         scr, +				    HB_Stream                 stream ) +{ +  HB_Error error; + +  HB_UShort               n, count; + +  HB_UShort*              c; +  HB_SubstLookupRecord*  slr; + + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  scr->GlyphCount = GET_UShort(); +  scr->SubstCount = GET_UShort(); + +  if ( scr->GlyphCount > csf2->MaxContextLength ) +    csf2->MaxContextLength = scr->GlyphCount; + +  FORGET_Frame(); + +  scr->Class = NULL; + +  count = scr->GlyphCount - 1;        /* only GlyphCount - 1 elements */ + +  if ( ALLOC_ARRAY( scr->Class, count, HB_UShort ) ) +    return error; + +  c = scr->Class; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail2; + +  for ( n = 0; n < count; n++ ) +    c[n] = GET_UShort(); + +  FORGET_Frame(); + +  scr->SubstLookupRecord = NULL; + +  count = scr->SubstCount; + +  if ( ALLOC_ARRAY( scr->SubstLookupRecord, count, HB_SubstLookupRecord ) ) +    goto Fail2; + +  slr = scr->SubstLookupRecord; + +  if ( ACCESS_Frame( count * 4L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +  { +    slr[n].SequenceIndex   = GET_UShort(); +    slr[n].LookupListIndex = GET_UShort(); +  } + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( slr ); + +Fail2: +  FREE( c ); +  return error; +} + + +static void  Free_SubClassRule( HB_SubClassRule*  scr ) +{ +  FREE( scr->SubstLookupRecord ); +  FREE( scr->Class ); +} + + +/* SubClassSet */ + +static HB_Error  Load_SubClassSet( HB_ContextSubstFormat2*  csf2, +				   HB_SubClassSet*          scs, +				   HB_Stream                 stream ) +{ +  HB_Error error; + +  HB_UShort          n = 0, m, count; +  HB_UInt           cur_offset, new_offset, base_offset; + +  HB_SubClassRule*  scr; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = scs->SubClassRuleCount = GET_UShort(); + +  FORGET_Frame(); + +  scs->SubClassRule = NULL; + +  if ( ALLOC_ARRAY( scs->SubClassRule, count, HB_SubClassRule ) ) +    return error; + +  scr = scs->SubClassRule; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_SubClassRule( csf2, &scr[n], +				      stream ) ) != HB_Err_Ok ) +      goto Fail; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail: +  for ( m = 0; m < n; m++ ) +    Free_SubClassRule( &scr[m] ); + +  FREE( scr ); +  return error; +} + + +static void  Free_SubClassSet( HB_SubClassSet*  scs ) +{ +  HB_UShort          n, count; + +  HB_SubClassRule*  scr; + + +  if ( scs->SubClassRule ) +  { +    count = scs->SubClassRuleCount; +    scr   = scs->SubClassRule; + +    for ( n = 0; n < count; n++ ) +      Free_SubClassRule( &scr[n] ); + +    FREE( scr ); +  } +} + + +/* ContextSubstFormat2 */ + +static HB_Error  Load_ContextSubst2( HB_ContextSubstFormat2*  csf2, +				     HB_Stream                 stream ) +{ +  HB_Error error; + +  HB_UShort         n = 0, m, count; +  HB_UInt          cur_offset, new_offset, base_offset; + +  HB_SubClassSet*  scs; + + +  base_offset = FILE_Pos() - 2; + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &csf2->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 4L ) ) +    goto Fail3; + +  new_offset = GET_UShort() + base_offset; + +  /* `SubClassSetCount' is the upper limit for class values, thus we +     read it now to make an additional safety check.                 */ + +  count = csf2->SubClassSetCount = GET_UShort(); + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_ClassDefinition( &csf2->ClassDef, count, +				       stream ) ) != HB_Err_Ok ) +    goto Fail3; +  (void)FILE_Seek( cur_offset ); + +  csf2->SubClassSet      = NULL; +  csf2->MaxContextLength = 0; + +  if ( ALLOC_ARRAY( csf2->SubClassSet, count, HB_SubClassSet ) ) +    goto Fail2; + +  scs = csf2->SubClassSet; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    if ( new_offset != base_offset )      /* not a NULL offset */ +    { +      cur_offset = FILE_Pos(); +      if ( FILE_Seek( new_offset ) || +	   ( error = Load_SubClassSet( csf2, &scs[n], +				       stream ) ) != HB_Err_Ok ) +	goto Fail1; +      (void)FILE_Seek( cur_offset ); +    } +    else +    { +      /* we create a SubClassSet table with no entries */ + +      csf2->SubClassSet[n].SubClassRuleCount = 0; +      csf2->SubClassSet[n].SubClassRule      = NULL; +    } +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_SubClassSet( &scs[m] ); + +  FREE( scs ); + +Fail2: +  _HB_OPEN_Free_ClassDefinition( &csf2->ClassDef ); + +Fail3: +  _HB_OPEN_Free_Coverage( &csf2->Coverage ); +  return error; +} + + +static void  Free_ContextSubst2( HB_ContextSubstFormat2*  csf2 ) +{ +  HB_UShort         n, count; + +  HB_SubClassSet*  scs; + + +  if ( csf2->SubClassSet ) +  { +    count = csf2->SubClassSetCount; +    scs   = csf2->SubClassSet; + +    for ( n = 0; n < count; n++ ) +      Free_SubClassSet( &scs[n] ); + +    FREE( scs ); +  } + +  _HB_OPEN_Free_ClassDefinition( &csf2->ClassDef ); +  _HB_OPEN_Free_Coverage( &csf2->Coverage ); +} + + +/* ContextSubstFormat3 */ + +static HB_Error  Load_ContextSubst3( HB_ContextSubstFormat3*  csf3, +				     HB_Stream                 stream ) +{ +  HB_Error error; + +  HB_UShort               n = 0, m, count; +  HB_UInt                cur_offset, new_offset, base_offset; + +  HB_Coverage*           c; +  HB_SubstLookupRecord*  slr; + + +  base_offset = FILE_Pos() - 2L; + +  if ( ACCESS_Frame( 4L ) ) +    return error; + +  csf3->GlyphCount = GET_UShort(); +  csf3->SubstCount = GET_UShort(); + +  FORGET_Frame(); + +  csf3->Coverage = NULL; + +  count = csf3->GlyphCount; + +  if ( ALLOC_ARRAY( csf3->Coverage, count, HB_Coverage ) ) +    return error; + +  c = csf3->Coverage; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail2; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != HB_Err_Ok ) +      goto Fail2; +    (void)FILE_Seek( cur_offset ); +  } + +  csf3->SubstLookupRecord = NULL; + +  count = csf3->SubstCount; + +  if ( ALLOC_ARRAY( csf3->SubstLookupRecord, count, +		    HB_SubstLookupRecord ) ) +    goto Fail2; + +  slr = csf3->SubstLookupRecord; + +  if ( ACCESS_Frame( count * 4L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +  { +    slr[n].SequenceIndex   = GET_UShort(); +    slr[n].LookupListIndex = GET_UShort(); +  } + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( slr ); + +Fail2: +  for ( m = 0; m < n; m++ ) +    _HB_OPEN_Free_Coverage( &c[m] ); + +  FREE( c ); +  return error; +} + + +static void  Free_ContextSubst3( HB_ContextSubstFormat3*  csf3 ) +{ +  HB_UShort      n, count; + +  HB_Coverage*  c; + + +  FREE( csf3->SubstLookupRecord ); + +  if ( csf3->Coverage ) +  { +    count = csf3->GlyphCount; +    c     = csf3->Coverage; + +    for ( n = 0; n < count; n++ ) +      _HB_OPEN_Free_Coverage( &c[n] ); + +    FREE( c ); +  } +} + + +/* ContextSubst */ + +static HB_Error  Load_ContextSubst( HB_GSUB_SubTable* st, +				    HB_Stream         stream ) +{ +  HB_Error error; +  HB_ContextSubst*  cs = &st->context; + + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  cs->SubstFormat = GET_UShort(); + +  FORGET_Frame(); + +  switch ( cs->SubstFormat ) +  { +  case 1:  return Load_ContextSubst1( &cs->csf.csf1, stream ); +  case 2:  return Load_ContextSubst2( &cs->csf.csf2, stream ); +  case 3:  return Load_ContextSubst3( &cs->csf.csf3, stream ); +  default: return ERR(HB_Err_Invalid_SubTable_Format); +  } + +  return HB_Err_Ok;               /* never reached */ +} + + +static void  Free_ContextSubst( HB_GSUB_SubTable* st ) +{ +  HB_ContextSubst*  cs = &st->context; + +  switch ( cs->SubstFormat ) +  { +  case 1:  Free_ContextSubst1( &cs->csf.csf1 ); break; +  case 2:  Free_ContextSubst2( &cs->csf.csf2 ); break; +  case 3:  Free_ContextSubst3( &cs->csf.csf3 ); break; +  default:						break; +  } +} + + +static HB_Error  Lookup_ContextSubst1( HB_GSUBHeader*          gsub, +				       HB_ContextSubstFormat1* csf1, +				       HB_Buffer               buffer, +				       HB_UShort                flags, +				       HB_UShort                context_length, +				       int                      nesting_level ) +{ +  HB_UShort        index, property; +  HB_UShort        i, j, k, numsr; +  HB_Error         error; + +  HB_SubRule*     sr; +  HB_GDEFHeader*  gdef; + + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  error = _HB_OPEN_Coverage_Index( &csf1->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  sr    = csf1->SubRuleSet[index].SubRule; +  numsr = csf1->SubRuleSet[index].SubRuleCount; + +  for ( k = 0; k < numsr; k++ ) +  { +    if ( context_length != 0xFFFF && context_length < sr[k].GlyphCount ) +      goto next_subrule; + +    if ( buffer->in_pos + sr[k].GlyphCount > buffer->in_length ) +      goto next_subrule;                        /* context is too long */ + +    for ( i = 1, j = buffer->in_pos + 1; i < sr[k].GlyphCount; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  return error; + +	if ( j + sr[k].GlyphCount - i == (HB_Int)buffer->in_length ) +	  goto next_subrule; +	j++; +      } + +      if ( IN_GLYPH( j ) != sr[k].Input[i - 1] ) +	goto next_subrule; +    } + +    return Do_ContextSubst( gsub, sr[k].GlyphCount, +			    sr[k].SubstCount, sr[k].SubstLookupRecord, +			    buffer, +			    nesting_level ); +  next_subrule: +    ; +  } + +  return HB_Err_Not_Covered; +} + + +static HB_Error  Lookup_ContextSubst2( HB_GSUBHeader*          gsub, +				       HB_ContextSubstFormat2* csf2, +				       HB_Buffer               buffer, +				       HB_UShort                flags, +				       HB_UShort                context_length, +				       int                      nesting_level ) +{ +  HB_UShort          index, property; +  HB_Error           error; +  HB_UShort          i, j, k, known_classes; + +  HB_UShort*         classes; +  HB_UShort*         cl; + +  HB_SubClassSet*   scs; +  HB_SubClassRule*  sr; +  HB_GDEFHeader*    gdef; + + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), 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 = _HB_OPEN_Coverage_Index( &csf2->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  if (csf2->MaxContextLength < 1) +    return HB_Err_Not_Covered; + +  if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, HB_UShort ) ) +    return error; + +  error = _HB_OPEN_Get_Class( &csf2->ClassDef, IN_CURGLYPH(), +		     &classes[0], NULL ); +  if ( error && error != HB_Err_Not_Covered ) +    goto End; +  known_classes = 0; + +  scs = &csf2->SubClassSet[classes[0]]; +  if ( !scs ) +  { +    error = ERR(HB_Err_Invalid_SubTable); +    goto End; +  } + +  for ( k = 0; k < scs->SubClassRuleCount; k++ ) +  { +    sr  = &scs->SubClassRule[k]; + +    if ( context_length != 0xFFFF && context_length < sr->GlyphCount ) +      goto next_subclassrule; + +    if ( buffer->in_pos + sr->GlyphCount > buffer->in_length ) +      goto next_subclassrule;                      /* context is too long */ + +    cl   = sr->Class; + +    /* Start at 1 because [0] is implied */ + +    for ( i = 1, j = buffer->in_pos + 1; i < sr->GlyphCount; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  goto End; + +	if ( j + sr->GlyphCount - i < (HB_Int)buffer->in_length ) +	  goto next_subclassrule; +	j++; +      } + +      if ( i > known_classes ) +      { +	/* Keeps us from having to do this for each rule */ + +	error = _HB_OPEN_Get_Class( &csf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL ); +	if ( error && error != HB_Err_Not_Covered ) +	  goto End; +	known_classes = i; +      } + +      if ( cl[i - 1] != classes[i] ) +	goto next_subclassrule; +    } + +    error = Do_ContextSubst( gsub, sr->GlyphCount, +			     sr->SubstCount, sr->SubstLookupRecord, +			     buffer, +			     nesting_level ); +    goto End; + +  next_subclassrule: +    ; +  } + +  error = HB_Err_Not_Covered; + +End: +  FREE( classes ); +  return error; +} + + +static HB_Error  Lookup_ContextSubst3( HB_GSUBHeader*          gsub, +				       HB_ContextSubstFormat3* csf3, +				       HB_Buffer               buffer, +				       HB_UShort                flags, +				       HB_UShort                context_length, +				       int                      nesting_level ) +{ +  HB_Error         error; +  HB_UShort        index, i, j, property; + +  HB_Coverage*    c; +  HB_GDEFHeader*  gdef; + + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  if ( context_length != 0xFFFF && context_length < csf3->GlyphCount ) +    return HB_Err_Not_Covered; + +  if ( buffer->in_pos + csf3->GlyphCount > buffer->in_length ) +    return HB_Err_Not_Covered;         /* context is too long */ + +  c    = csf3->Coverage; + +  for ( i = 1, j = buffer->in_pos + 1; i < csf3->GlyphCount; i++, j++ ) +  { +    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +    { +      if ( error && error != HB_Err_Not_Covered ) +	return error; + +      if ( j + csf3->GlyphCount - i == (HB_Int)buffer->in_length ) +	return HB_Err_Not_Covered; +      j++; +    } + +    error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index ); +    if ( error ) +      return error; +  } + +  return Do_ContextSubst( gsub, csf3->GlyphCount, +			  csf3->SubstCount, csf3->SubstLookupRecord, +			  buffer, +			  nesting_level ); +} + + +static HB_Error  Lookup_ContextSubst( HB_GSUBHeader*    gsub, +				      HB_GSUB_SubTable* st, +				      HB_Buffer         buffer, +				      HB_UShort          flags, +				      HB_UShort          context_length, +				      int                nesting_level ) +{ +  HB_ContextSubst*  cs = &st->context; + +  switch ( cs->SubstFormat ) +  { +  case 1:  return Lookup_ContextSubst1( gsub, &cs->csf.csf1, buffer, flags, context_length, nesting_level ); +  case 2:  return Lookup_ContextSubst2( gsub, &cs->csf.csf2, buffer, flags, context_length, nesting_level ); +  case 3:  return Lookup_ContextSubst3( gsub, &cs->csf.csf3, buffer, flags, context_length, nesting_level ); +  default: return ERR(HB_Err_Invalid_SubTable_Format); +  } + +  return HB_Err_Ok;               /* never reached */ +} + + +/* LookupType 6 */ + +/* ChainSubRule */ + +static HB_Error  Load_ChainSubRule( HB_ChainSubRule*  csr, +				    HB_Stream          stream ) +{ +  HB_Error error; + +  HB_UShort               n, count; +  HB_UShort*              b; +  HB_UShort*              i; +  HB_UShort*              l; + +  HB_SubstLookupRecord*  slr; + + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  csr->BacktrackGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  csr->Backtrack = NULL; + +  count = csr->BacktrackGlyphCount; + +  if ( ALLOC_ARRAY( csr->Backtrack, count, HB_UShort ) ) +    return error; + +  b = csr->Backtrack; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail4; + +  for ( n = 0; n < count; n++ ) +    b[n] = GET_UShort(); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail4; + +  csr->InputGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  csr->Input = NULL; + +  count = csr->InputGlyphCount - 1;  /* only InputGlyphCount - 1 elements */ + +  if ( ALLOC_ARRAY( csr->Input, count, HB_UShort ) ) +    goto Fail4; + +  i = csr->Input; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail3; + +  for ( n = 0; n < count; n++ ) +    i[n] = GET_UShort(); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail3; + +  csr->LookaheadGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  csr->Lookahead = NULL; + +  count = csr->LookaheadGlyphCount; + +  if ( ALLOC_ARRAY( csr->Lookahead, count, HB_UShort ) ) +    goto Fail3; + +  l = csr->Lookahead; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail2; + +  for ( n = 0; n < count; n++ ) +    l[n] = GET_UShort(); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  csr->SubstCount = GET_UShort(); + +  FORGET_Frame(); + +  csr->SubstLookupRecord = NULL; + +  count = csr->SubstCount; + +  if ( ALLOC_ARRAY( csr->SubstLookupRecord, count, HB_SubstLookupRecord ) ) +    goto Fail2; + +  slr = csr->SubstLookupRecord; + +  if ( ACCESS_Frame( count * 4L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +  { +    slr[n].SequenceIndex   = GET_UShort(); +    slr[n].LookupListIndex = GET_UShort(); +  } + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( slr ); + +Fail2: +  FREE( l ); + +Fail3: +  FREE( i ); + +Fail4: +  FREE( b ); +  return error; +} + + +static void  Free_ChainSubRule( HB_ChainSubRule*  csr ) +{ +  FREE( csr->SubstLookupRecord ); +  FREE( csr->Lookahead ); +  FREE( csr->Input ); +  FREE( csr->Backtrack ); +} + + +/* ChainSubRuleSet */ + +static HB_Error  Load_ChainSubRuleSet( HB_ChainSubRuleSet*  csrs, +				       HB_Stream             stream ) +{ +  HB_Error error; + +  HB_UShort          n = 0, m, count; +  HB_UInt           cur_offset, new_offset, base_offset; + +  HB_ChainSubRule*  csr; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = csrs->ChainSubRuleCount = GET_UShort(); + +  FORGET_Frame(); + +  csrs->ChainSubRule = NULL; + +  if ( ALLOC_ARRAY( csrs->ChainSubRule, count, HB_ChainSubRule ) ) +    return error; + +  csr = csrs->ChainSubRule; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_ChainSubRule( &csr[n], stream ) ) != HB_Err_Ok ) +      goto Fail; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail: +  for ( m = 0; m < n; m++ ) +    Free_ChainSubRule( &csr[m] ); + +  FREE( csr ); +  return error; +} + + +static void  Free_ChainSubRuleSet( HB_ChainSubRuleSet*  csrs ) +{ +  HB_UShort          n, count; + +  HB_ChainSubRule*  csr; + + +  if ( csrs->ChainSubRule ) +  { +    count = csrs->ChainSubRuleCount; +    csr   = csrs->ChainSubRule; + +    for ( n = 0; n < count; n++ ) +      Free_ChainSubRule( &csr[n] ); + +    FREE( csr ); +  } +} + + +/* ChainContextSubstFormat1 */ + +static HB_Error  Load_ChainContextSubst1( +		   HB_ChainContextSubstFormat1*  ccsf1, +		   HB_Stream                      stream ) +{ +  HB_Error error; + +  HB_UShort             n = 0, m, count; +  HB_UInt              cur_offset, new_offset, base_offset; + +  HB_ChainSubRuleSet*  csrs; + + +  base_offset = FILE_Pos() - 2L; + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &ccsf1->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  count = ccsf1->ChainSubRuleSetCount = GET_UShort(); + +  FORGET_Frame(); + +  ccsf1->ChainSubRuleSet = NULL; + +  if ( ALLOC_ARRAY( ccsf1->ChainSubRuleSet, count, HB_ChainSubRuleSet ) ) +    goto Fail2; + +  csrs = ccsf1->ChainSubRuleSet; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_ChainSubRuleSet( &csrs[n], stream ) ) != HB_Err_Ok ) +      goto Fail1; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_ChainSubRuleSet( &csrs[m] ); + +  FREE( csrs ); + +Fail2: +  _HB_OPEN_Free_Coverage( &ccsf1->Coverage ); +  return error; +} + + +static void  Free_ChainContextSubst1( HB_ChainContextSubstFormat1*  ccsf1 ) +{ +  HB_UShort             n, count; + +  HB_ChainSubRuleSet*  csrs; + + +  if ( ccsf1->ChainSubRuleSet ) +  { +    count = ccsf1->ChainSubRuleSetCount; +    csrs  = ccsf1->ChainSubRuleSet; + +    for ( n = 0; n < count; n++ ) +      Free_ChainSubRuleSet( &csrs[n] ); + +    FREE( csrs ); +  } + +  _HB_OPEN_Free_Coverage( &ccsf1->Coverage ); +} + + +/* ChainSubClassRule */ + +static HB_Error  Load_ChainSubClassRule( +		   HB_ChainContextSubstFormat2*  ccsf2, +		   HB_ChainSubClassRule*         cscr, +		   HB_Stream                      stream ) +{ +  HB_Error error; + +  HB_UShort               n, count; + +  HB_UShort*              b; +  HB_UShort*              i; +  HB_UShort*              l; +  HB_SubstLookupRecord*  slr; + + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  cscr->BacktrackGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  if ( cscr->BacktrackGlyphCount > ccsf2->MaxBacktrackLength ) +    ccsf2->MaxBacktrackLength = cscr->BacktrackGlyphCount; + +  cscr->Backtrack = NULL; + +  count = cscr->BacktrackGlyphCount; + +  if ( ALLOC_ARRAY( cscr->Backtrack, count, HB_UShort ) ) +    return error; + +  b = cscr->Backtrack; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail4; + +  for ( n = 0; n < count; n++ ) +    b[n] = GET_UShort(); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail4; + +  cscr->InputGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  if ( cscr->InputGlyphCount > ccsf2->MaxInputLength ) +    ccsf2->MaxInputLength = cscr->InputGlyphCount; + +  cscr->Input = NULL; + +  count = cscr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */ + +  if ( ALLOC_ARRAY( cscr->Input, count, HB_UShort ) ) +    goto Fail4; + +  i = cscr->Input; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail3; + +  for ( n = 0; n < count; n++ ) +    i[n] = GET_UShort(); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail3; + +  cscr->LookaheadGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  if ( cscr->LookaheadGlyphCount > ccsf2->MaxLookaheadLength ) +    ccsf2->MaxLookaheadLength = cscr->LookaheadGlyphCount; + +  cscr->Lookahead = NULL; + +  count = cscr->LookaheadGlyphCount; + +  if ( ALLOC_ARRAY( cscr->Lookahead, count, HB_UShort ) ) +    goto Fail3; + +  l = cscr->Lookahead; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail2; + +  for ( n = 0; n < count; n++ ) +    l[n] = GET_UShort(); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  cscr->SubstCount = GET_UShort(); + +  FORGET_Frame(); + +  cscr->SubstLookupRecord = NULL; + +  count = cscr->SubstCount; + +  if ( ALLOC_ARRAY( cscr->SubstLookupRecord, count, +		    HB_SubstLookupRecord ) ) +    goto Fail2; + +  slr = cscr->SubstLookupRecord; + +  if ( ACCESS_Frame( count * 4L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +  { +    slr[n].SequenceIndex   = GET_UShort(); +    slr[n].LookupListIndex = GET_UShort(); +  } + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( slr ); + +Fail2: +  FREE( l ); + +Fail3: +  FREE( i ); + +Fail4: +  FREE( b ); +  return error; +} + + +static void  Free_ChainSubClassRule( HB_ChainSubClassRule*  cscr ) +{ +  FREE( cscr->SubstLookupRecord ); +  FREE( cscr->Lookahead ); +  FREE( cscr->Input ); +  FREE( cscr->Backtrack ); +} + + +/* SubClassSet */ + +static HB_Error  Load_ChainSubClassSet( +		   HB_ChainContextSubstFormat2*  ccsf2, +		   HB_ChainSubClassSet*          cscs, +		   HB_Stream                      stream ) +{ +  HB_Error error; + +  HB_UShort               n = 0, m, count; +  HB_UInt                cur_offset, new_offset, base_offset; + +  HB_ChainSubClassRule*  cscr; + + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  count = cscs->ChainSubClassRuleCount = GET_UShort(); + +  FORGET_Frame(); + +  cscs->ChainSubClassRule = NULL; + +  if ( ALLOC_ARRAY( cscs->ChainSubClassRule, count, +		    HB_ChainSubClassRule ) ) +    return error; + +  cscr = cscs->ChainSubClassRule; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = Load_ChainSubClassRule( ccsf2, &cscr[n], +					   stream ) ) != HB_Err_Ok ) +      goto Fail; +    (void)FILE_Seek( cur_offset ); +  } + +  return HB_Err_Ok; + +Fail: +  for ( m = 0; m < n; m++ ) +    Free_ChainSubClassRule( &cscr[m] ); + +  FREE( cscr ); +  return error; +} + + +static void  Free_ChainSubClassSet( HB_ChainSubClassSet*  cscs ) +{ +  HB_UShort               n, count; + +  HB_ChainSubClassRule*  cscr; + + +  if ( cscs->ChainSubClassRule ) +  { +    count = cscs->ChainSubClassRuleCount; +    cscr  = cscs->ChainSubClassRule; + +    for ( n = 0; n < count; n++ ) +      Free_ChainSubClassRule( &cscr[n] ); + +    FREE( cscr ); +  } +} + + +/* ChainContextSubstFormat2 */ + +static HB_Error  Load_ChainContextSubst2( +		   HB_ChainContextSubstFormat2*  ccsf2, +		   HB_Stream                      stream ) +{ +  HB_Error error; + +  HB_UShort              n = 0, m, count; +  HB_UInt               cur_offset, new_offset, base_offset; +  HB_UInt               backtrack_offset, input_offset, lookahead_offset; + +  HB_ChainSubClassSet*  cscs; + + +  base_offset = FILE_Pos() - 2; + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &ccsf2->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + +  if ( ACCESS_Frame( 8L ) ) +    goto Fail5; + +  backtrack_offset = GET_UShort(); +  input_offset     = GET_UShort(); +  lookahead_offset = GET_UShort(); + +  /* `ChainSubClassSetCount' is the upper limit for input class values, +     thus we read it now to make an additional safety check. No limit +     is known or needed for the other two class definitions          */ + +  count = ccsf2->ChainSubClassSetCount = GET_UShort(); + +  FORGET_Frame(); + +  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->BacktrackClassDef, 65535, +						       backtrack_offset, base_offset, +						       stream ) ) != HB_Err_Ok ) +      goto Fail5; + +  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->InputClassDef, count, +						       input_offset, base_offset, +						       stream ) ) != HB_Err_Ok ) +      goto Fail4; +  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->LookaheadClassDef, 65535, +						       lookahead_offset, base_offset, +						       stream ) ) != HB_Err_Ok ) +    goto Fail3; + +  ccsf2->ChainSubClassSet   = NULL; +  ccsf2->MaxBacktrackLength = 0; +  ccsf2->MaxInputLength     = 0; +  ccsf2->MaxLookaheadLength = 0; + +  if ( ALLOC_ARRAY( ccsf2->ChainSubClassSet, count, HB_ChainSubClassSet ) ) +    goto Fail2; + +  cscs = ccsf2->ChainSubClassSet; + +  for ( n = 0; n < count; n++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail1; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    if ( new_offset != base_offset )      /* not a NULL offset */ +    { +      cur_offset = FILE_Pos(); +      if ( FILE_Seek( new_offset ) || +	   ( error = Load_ChainSubClassSet( ccsf2, &cscs[n], +					    stream ) ) != HB_Err_Ok ) +	goto Fail1; +      (void)FILE_Seek( cur_offset ); +    } +    else +    { +      /* we create a ChainSubClassSet table with no entries */ + +      ccsf2->ChainSubClassSet[n].ChainSubClassRuleCount = 0; +      ccsf2->ChainSubClassSet[n].ChainSubClassRule      = NULL; +    } +  } + +  return HB_Err_Ok; + +Fail1: +  for ( m = 0; m < n; m++ ) +    Free_ChainSubClassSet( &cscs[m] ); + +  FREE( cscs ); + +Fail2: +  _HB_OPEN_Free_ClassDefinition( &ccsf2->LookaheadClassDef ); + +Fail3: +  _HB_OPEN_Free_ClassDefinition( &ccsf2->InputClassDef ); + +Fail4: +  _HB_OPEN_Free_ClassDefinition( &ccsf2->BacktrackClassDef ); + +Fail5: +  _HB_OPEN_Free_Coverage( &ccsf2->Coverage ); +  return error; +} + + +static void  Free_ChainContextSubst2( HB_ChainContextSubstFormat2*  ccsf2 ) +{ +  HB_UShort              n, count; + +  HB_ChainSubClassSet*  cscs; + + +  if ( ccsf2->ChainSubClassSet ) +  { +    count = ccsf2->ChainSubClassSetCount; +    cscs  = ccsf2->ChainSubClassSet; + +    for ( n = 0; n < count; n++ ) +      Free_ChainSubClassSet( &cscs[n] ); + +    FREE( cscs ); +  } + +  _HB_OPEN_Free_ClassDefinition( &ccsf2->LookaheadClassDef ); +  _HB_OPEN_Free_ClassDefinition( &ccsf2->InputClassDef ); +  _HB_OPEN_Free_ClassDefinition( &ccsf2->BacktrackClassDef ); + +  _HB_OPEN_Free_Coverage( &ccsf2->Coverage ); +} + + +/* ChainContextSubstFormat3 */ + +static HB_Error  Load_ChainContextSubst3( +		   HB_ChainContextSubstFormat3*  ccsf3, +		   HB_Stream                      stream ) +{ +  HB_Error error; + +  HB_UShort               n, nb = 0, ni =0, nl = 0, m, count; +  HB_UShort               backtrack_count, input_count, lookahead_count; +  HB_UInt                cur_offset, new_offset, base_offset; + +  HB_Coverage*           b; +  HB_Coverage*           i; +  HB_Coverage*           l; +  HB_SubstLookupRecord*  slr; + + +  base_offset = FILE_Pos() - 2L; + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  ccsf3->BacktrackGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  ccsf3->BacktrackCoverage = NULL; + +  backtrack_count = ccsf3->BacktrackGlyphCount; + +  if ( ALLOC_ARRAY( ccsf3->BacktrackCoverage, backtrack_count, +		    HB_Coverage ) ) +    return error; + +  b = ccsf3->BacktrackCoverage; + +  for ( nb = 0; nb < backtrack_count; nb++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail4; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok ) +      goto Fail4; +    (void)FILE_Seek( cur_offset ); +  } + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail4; + +  ccsf3->InputGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  ccsf3->InputCoverage = NULL; + +  input_count = ccsf3->InputGlyphCount; + +  if ( ALLOC_ARRAY( ccsf3->InputCoverage, input_count, HB_Coverage ) ) +    goto Fail4; + +  i = ccsf3->InputCoverage; + +  for ( ni = 0; ni < input_count; ni++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail3; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != HB_Err_Ok ) +      goto Fail3; +    (void)FILE_Seek( cur_offset ); +  } + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail3; + +  ccsf3->LookaheadGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  ccsf3->LookaheadCoverage = NULL; + +  lookahead_count = ccsf3->LookaheadGlyphCount; + +  if ( ALLOC_ARRAY( ccsf3->LookaheadCoverage, lookahead_count, +		    HB_Coverage ) ) +    goto Fail3; + +  l = ccsf3->LookaheadCoverage; + +  for ( nl = 0; nl < lookahead_count; nl++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail2; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok ) +      goto Fail2; +    (void)FILE_Seek( cur_offset ); +  } + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  ccsf3->SubstCount = GET_UShort(); + +  FORGET_Frame(); + +  ccsf3->SubstLookupRecord = NULL; + +  count = ccsf3->SubstCount; + +  if ( ALLOC_ARRAY( ccsf3->SubstLookupRecord, count, +		    HB_SubstLookupRecord ) ) +    goto Fail2; + +  slr = ccsf3->SubstLookupRecord; + +  if ( ACCESS_Frame( count * 4L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +  { +    slr[n].SequenceIndex   = GET_UShort(); +    slr[n].LookupListIndex = GET_UShort(); +  } + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( slr ); + +Fail2: +  for ( m = 0; m < nl; m++ ) +    _HB_OPEN_Free_Coverage( &l[m] ); + +  FREE( l ); + +Fail3: +  for ( m = 0; m < ni; m++ ) +    _HB_OPEN_Free_Coverage( &i[m] ); + +  FREE( i ); + +Fail4: +  for ( m = 0; m < nb; m++ ) +    _HB_OPEN_Free_Coverage( &b[m] ); + +  FREE( b ); +  return error; +} + + +static void  Free_ChainContextSubst3( HB_ChainContextSubstFormat3*  ccsf3 ) +{ +  HB_UShort      n, count; + +  HB_Coverage*  c; + + +  FREE( ccsf3->SubstLookupRecord ); + +  if ( ccsf3->LookaheadCoverage ) +  { +    count = ccsf3->LookaheadGlyphCount; +    c     = ccsf3->LookaheadCoverage; + +    for ( n = 0; n < count; n++ ) +      _HB_OPEN_Free_Coverage( &c[n] ); + +    FREE( c ); +  } + +  if ( ccsf3->InputCoverage ) +  { +    count = ccsf3->InputGlyphCount; +    c     = ccsf3->InputCoverage; + +    for ( n = 0; n < count; n++ ) +      _HB_OPEN_Free_Coverage( &c[n] ); + +    FREE( c ); +  } + +  if ( ccsf3->BacktrackCoverage ) +  { +    count = ccsf3->BacktrackGlyphCount; +    c     = ccsf3->BacktrackCoverage; + +    for ( n = 0; n < count; n++ ) +      _HB_OPEN_Free_Coverage( &c[n] ); + +    FREE( c ); +  } +} + + +/* ChainContextSubst */ + +static HB_Error  Load_ChainContextSubst( HB_GSUB_SubTable* st, +					 HB_Stream         stream ) +{ +  HB_Error error; +  HB_ChainContextSubst*  ccs = &st->chain; + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  ccs->SubstFormat = GET_UShort(); + +  FORGET_Frame(); + +  switch ( ccs->SubstFormat ) { +    case 1:  return Load_ChainContextSubst1( &ccs->ccsf.ccsf1, stream ); +    case 2:  return Load_ChainContextSubst2( &ccs->ccsf.ccsf2, stream ); +    case 3:  return Load_ChainContextSubst3( &ccs->ccsf.ccsf3, stream ); +    default: return ERR(HB_Err_Invalid_SubTable_Format); +  } + +  return HB_Err_Ok;               /* never reached */ +} + + +static void  Free_ChainContextSubst( HB_GSUB_SubTable* st ) +{ +  HB_ChainContextSubst*  ccs = &st->chain; + +  switch ( ccs->SubstFormat ) { +    case 1:  Free_ChainContextSubst1( &ccs->ccsf.ccsf1 ); break; +    case 2:  Free_ChainContextSubst2( &ccs->ccsf.ccsf2 ); break; +    case 3:  Free_ChainContextSubst3( &ccs->ccsf.ccsf3 ); break; +    default:							  break; +  } +} + + +static HB_Error  Lookup_ChainContextSubst1( HB_GSUBHeader*               gsub, +					    HB_ChainContextSubstFormat1* ccsf1, +					    HB_Buffer                    buffer, +					    HB_UShort                     flags, +					    HB_UShort                     context_length, +					    int                           nesting_level ) +{ +  HB_UShort          index, property; +  HB_UShort          i, j, k, num_csr; +  HB_UShort          bgc, igc, lgc; +  HB_Error           error; + +  HB_ChainSubRule*  csr; +  HB_ChainSubRule   curr_csr; +  HB_GDEFHeader*    gdef; + + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  error = _HB_OPEN_Coverage_Index( &ccsf1->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  csr     = ccsf1->ChainSubRuleSet[index].ChainSubRule; +  num_csr = ccsf1->ChainSubRuleSet[index].ChainSubRuleCount; + +  for ( k = 0; k < num_csr; k++ ) +  { +    curr_csr = csr[k]; +    bgc      = curr_csr.BacktrackGlyphCount; +    igc      = curr_csr.InputGlyphCount; +    lgc      = curr_csr.LookaheadGlyphCount; + +    if ( context_length != 0xFFFF && context_length < igc ) +      goto next_chainsubrule; + +    /* check whether context is too long; it is a first guess only */ + +    if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length ) +      goto next_chainsubrule; + +    if ( bgc ) +    { +      /* since we don't know in advance the number of glyphs to inspect, +	 we search backwards for matches in the backtrack glyph array    */ + +      for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- ) +      { +	while ( CHECK_Property( gdef, OUT_ITEM( j ), flags, &property ) ) +	{ +	  if ( error && error != HB_Err_Not_Covered ) +	    return error; + +	  if ( j + 1 == bgc - i ) +	    goto next_chainsubrule; +	  j--; +	} + +	/* In OpenType 1.3, it is undefined whether the offsets of +	   backtrack glyphs is in logical order or not.  Version 1.4 +	   will clarify this: + +	     Logical order -      a  b  c  d  e  f  g  h  i  j +					      i +	     Input offsets -                  0  1 +	     Backtrack offsets -  3  2  1  0 +	     Lookahead offsets -                    0  1  2  3           */ + +	if ( OUT_GLYPH( j ) != curr_csr.Backtrack[i] ) +	  goto next_chainsubrule; +      } +    } + +    /* Start at 1 because [0] is implied */ + +    for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  return error; + +	if ( j + igc - i + lgc == (HB_Int)buffer->in_length ) +	  goto next_chainsubrule; +	j++; +      } + +      if ( IN_GLYPH( j ) != curr_csr.Input[i - 1] ) +	  goto next_chainsubrule; +    } + +    /* we are starting to check for lookahead glyphs right after the +       last context glyph                                            */ + +    for ( i = 0; i < lgc; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  return error; + +	if ( j + lgc - i == (HB_Int)buffer->in_length ) +	  goto next_chainsubrule; +	j++; +      } + +      if ( IN_GLYPH( j ) != curr_csr.Lookahead[i] ) +	goto next_chainsubrule; +    } + +    return Do_ContextSubst( gsub, igc, +			    curr_csr.SubstCount, +			    curr_csr.SubstLookupRecord, +			    buffer, +			    nesting_level ); + +  next_chainsubrule: +    ; +  } + +  return HB_Err_Not_Covered; +} + + +static HB_Error  Lookup_ChainContextSubst2( HB_GSUBHeader*               gsub, +					    HB_ChainContextSubstFormat2* ccsf2, +					    HB_Buffer                    buffer, +					    HB_UShort                     flags, +					    HB_UShort                     context_length, +					    int                           nesting_level ) +{ +  HB_UShort              index, property; +  HB_Error               error; +  HB_UShort              i, j, k; +  HB_UShort              bgc, igc, lgc; +  HB_UShort              known_backtrack_classes, +			 known_input_classes, +			 known_lookahead_classes; + +  HB_UShort*             backtrack_classes; +  HB_UShort*             input_classes; +  HB_UShort*             lookahead_classes; + +  HB_UShort*             bc; +  HB_UShort*             ic; +  HB_UShort*             lc; + +  HB_ChainSubClassSet*  cscs; +  HB_ChainSubClassRule  ccsr; +  HB_GDEFHeader*        gdef; + + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), 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 = _HB_OPEN_Coverage_Index( &ccsf2->Coverage, IN_CURGLYPH(), &index ); +  if ( error ) +    return error; + +  if ( ALLOC_ARRAY( backtrack_classes, ccsf2->MaxBacktrackLength, HB_UShort ) ) +    return error; +  known_backtrack_classes = 0; + +  if (ccsf2->MaxInputLength < 1) +    return HB_Err_Not_Covered; + +  if ( ALLOC_ARRAY( input_classes, ccsf2->MaxInputLength, HB_UShort ) ) +    goto End3; +  known_input_classes = 1; + +  if ( ALLOC_ARRAY( lookahead_classes, ccsf2->MaxLookaheadLength, HB_UShort ) ) +    goto End2; +  known_lookahead_classes = 0; + +  error = _HB_OPEN_Get_Class( &ccsf2->InputClassDef, IN_CURGLYPH(), +		     &input_classes[0], NULL ); +  if ( error && error != HB_Err_Not_Covered ) +    goto End1; + +  cscs = &ccsf2->ChainSubClassSet[input_classes[0]]; +  if ( !cscs ) +  { +    error = ERR(HB_Err_Invalid_SubTable); +    goto End1; +  } + +  for ( k = 0; k < cscs->ChainSubClassRuleCount; k++ ) +  { +    ccsr = cscs->ChainSubClassRule[k]; +    bgc  = ccsr.BacktrackGlyphCount; +    igc  = ccsr.InputGlyphCount; +    lgc  = ccsr.LookaheadGlyphCount; + +    if ( context_length != 0xFFFF && context_length < igc ) +      goto next_chainsubclassrule; + +    /* check whether context is too long; it is a first guess only */ + +    if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length ) +      goto next_chainsubclassrule; + +    if ( bgc ) +    { +      /* Since we don't know in advance the number of glyphs to inspect, +	 we search backwards for matches in the backtrack glyph array. +	 Note that `known_backtrack_classes' starts at index 0.         */ + +      bc       = ccsr.Backtrack; + +      for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- ) +      { +	while ( CHECK_Property( gdef, OUT_ITEM( j ), flags, &property ) ) +	{ +	  if ( error && error != HB_Err_Not_Covered ) +	    goto End1; + +	  if ( j + 1 == bgc - i ) +	    goto next_chainsubclassrule; +	  j--; +	} + +	if ( i >= known_backtrack_classes ) +	{ +	  /* Keeps us from having to do this for each rule */ + +	  error = _HB_OPEN_Get_Class( &ccsf2->BacktrackClassDef, OUT_GLYPH( j ), +			     &backtrack_classes[i], NULL ); +	  if ( error && error != HB_Err_Not_Covered ) +	    goto End1; +	  known_backtrack_classes = i; +	} + +	if ( bc[i] != backtrack_classes[i] ) +	  goto next_chainsubclassrule; +      } +    } + +    ic       = ccsr.Input; + +    /* Start at 1 because [0] is implied */ + +    for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  goto End1; + +	if ( j + igc - i + lgc == (HB_Int)buffer->in_length ) +	  goto next_chainsubclassrule; +	j++; +      } + +      if ( i >= known_input_classes ) +      { +	error = _HB_OPEN_Get_Class( &ccsf2->InputClassDef, IN_GLYPH( j ), +			   &input_classes[i], NULL ); +	if ( error && error != HB_Err_Not_Covered ) +	  goto End1; +	known_input_classes = i; +      } + +      if ( ic[i - 1] != input_classes[i] ) +	goto next_chainsubclassrule; +    } + +    /* we are starting to check for lookahead glyphs right after the +       last context glyph                                            */ + +    lc       = ccsr.Lookahead; + +    for ( i = 0; i < lgc; i++, j++ ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  goto End1; + +	if ( j + lgc - i == (HB_Int)buffer->in_length ) +	  goto next_chainsubclassrule; +	j++; +      } + +      if ( i >= known_lookahead_classes ) +      { +	error = _HB_OPEN_Get_Class( &ccsf2->LookaheadClassDef, IN_GLYPH( j ), +			   &lookahead_classes[i], NULL ); +	if ( error && error != HB_Err_Not_Covered ) +	  goto End1; +	known_lookahead_classes = i; +      } + +      if ( lc[i] != lookahead_classes[i] ) +	goto next_chainsubclassrule; +    } + +    error = Do_ContextSubst( gsub, igc, +			     ccsr.SubstCount, +			     ccsr.SubstLookupRecord, +			     buffer, +			     nesting_level ); +    goto End1; + +  next_chainsubclassrule: +    ; +  } + +  error = HB_Err_Not_Covered; + +End1: +  FREE( lookahead_classes ); + +End2: +  FREE( input_classes ); + +End3: +  FREE( backtrack_classes ); +  return error; +} + + +static HB_Error  Lookup_ChainContextSubst3( HB_GSUBHeader*               gsub, +					    HB_ChainContextSubstFormat3* ccsf3, +					    HB_Buffer                    buffer, +					    HB_UShort                     flags, +					    HB_UShort                     context_length, +					    int                           nesting_level ) +{ +  HB_UShort        index, i, j, property; +  HB_UShort        bgc, igc, lgc; +  HB_Error         error; + +  HB_Coverage*    bc; +  HB_Coverage*    ic; +  HB_Coverage*    lc; +  HB_GDEFHeader*  gdef; + + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  bgc = ccsf3->BacktrackGlyphCount; +  igc = ccsf3->InputGlyphCount; +  lgc = ccsf3->LookaheadGlyphCount; + +  if ( context_length != 0xFFFF && context_length < igc ) +    return HB_Err_Not_Covered; + +  /* check whether context is too long; it is a first guess only */ + +  if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length ) +    return HB_Err_Not_Covered; + +  if ( bgc ) +  { +    /* Since we don't know in advance the number of glyphs to inspect, +       we search backwards for matches in the backtrack glyph array    */ + +    bc       = ccsf3->BacktrackCoverage; + +    for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- ) +    { +      while ( CHECK_Property( gdef, OUT_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  return error; + +	if ( j + 1 == bgc - i ) +	  return HB_Err_Not_Covered; +	j--; +      } + +      error = _HB_OPEN_Coverage_Index( &bc[i], OUT_GLYPH( j ), &index ); +      if ( error ) +	return error; +    } +  } + +  ic       = ccsf3->InputCoverage; + +  for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ ) +  { +    /* We already called CHECK_Property for IN_GLYPH( buffer->in_pos ) */ +    while ( j > buffer->in_pos && CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +    { +      if ( error && error != HB_Err_Not_Covered ) +	return error; + +      if ( j + igc - i + lgc == (HB_Int)buffer->in_length ) +	return HB_Err_Not_Covered; +      j++; +    } + +    error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index ); +    if ( error ) +      return error; +  } + +  /* we are starting for lookahead glyphs right after the last context +     glyph                                                             */ + +  lc       = ccsf3->LookaheadCoverage; + +  for ( i = 0; i < lgc; i++, j++ ) +  { +    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +    { +      if ( error && error != HB_Err_Not_Covered ) +	return error; + +      if ( j + lgc - i == (HB_Int)buffer->in_length ) +	return HB_Err_Not_Covered; +      j++; +    } + +    error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index ); +    if ( error ) +      return error; +  } + +  return Do_ContextSubst( gsub, igc, +			  ccsf3->SubstCount, +			  ccsf3->SubstLookupRecord, +			  buffer, +			  nesting_level ); +} + + +static HB_Error  Lookup_ChainContextSubst( HB_GSUBHeader*    gsub, +					   HB_GSUB_SubTable* st, +					   HB_Buffer         buffer, +					   HB_UShort          flags, +					   HB_UShort          context_length, +					   int                nesting_level ) +{ +  HB_ChainContextSubst*  ccs = &st->chain; + +  switch ( ccs->SubstFormat ) { +    case 1:  return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, buffer, flags, context_length, nesting_level ); +    case 2:  return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, buffer, flags, context_length, nesting_level ); +    case 3:  return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, buffer, flags, context_length, nesting_level ); +    default: return ERR(HB_Err_Invalid_SubTable_Format); +  } +} + + +static HB_Error  Load_ReverseChainContextSubst( HB_GSUB_SubTable* st, +					        HB_Stream         stream ) +{ +  HB_Error error; +  HB_ReverseChainContextSubst*  rccs = &st->reverse; + +  HB_UShort               m, count; + +  HB_UShort               nb = 0, nl = 0, n; +  HB_UShort               backtrack_count, lookahead_count; +  HB_UInt                cur_offset, new_offset, base_offset; + +  HB_Coverage*           b; +  HB_Coverage*           l; +  HB_UShort*              sub; + +  base_offset = FILE_Pos(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  rccs->SubstFormat = GET_UShort(); + +  if ( rccs->SubstFormat != 1 ) +    return ERR(HB_Err_Invalid_SubTable_Format); + +  FORGET_Frame(); + +  if ( ACCESS_Frame( 2L ) ) +    return error; + +  new_offset = GET_UShort() + base_offset; + +  FORGET_Frame(); + +  cur_offset = FILE_Pos(); +  if ( FILE_Seek( new_offset ) || +       ( error = _HB_OPEN_Load_Coverage( &rccs->Coverage, stream ) ) != HB_Err_Ok ) +    return error; +  (void)FILE_Seek( cur_offset ); + + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail4; + +  rccs->BacktrackGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  rccs->BacktrackCoverage = NULL; + +  backtrack_count = rccs->BacktrackGlyphCount; + +  if ( ALLOC_ARRAY( rccs->BacktrackCoverage, backtrack_count, +		    HB_Coverage ) ) +    goto Fail4; + +  b = rccs->BacktrackCoverage; + +  for ( nb = 0; nb < backtrack_count; nb++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail3; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok ) +      goto Fail3; +    (void)FILE_Seek( cur_offset ); +  } + + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail3; + +  rccs->LookaheadGlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  rccs->LookaheadCoverage = NULL; + +  lookahead_count = rccs->LookaheadGlyphCount; + +  if ( ALLOC_ARRAY( rccs->LookaheadCoverage, lookahead_count, +		    HB_Coverage ) ) +    goto Fail3; + +  l = rccs->LookaheadCoverage; + +  for ( nl = 0; nl < lookahead_count; nl++ ) +  { +    if ( ACCESS_Frame( 2L ) ) +      goto Fail2; + +    new_offset = GET_UShort() + base_offset; + +    FORGET_Frame(); + +    cur_offset = FILE_Pos(); +    if ( FILE_Seek( new_offset ) || +	 ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok ) +      goto Fail2; +    (void)FILE_Seek( cur_offset ); +  } + +  if ( ACCESS_Frame( 2L ) ) +    goto Fail2; + +  rccs->GlyphCount = GET_UShort(); + +  FORGET_Frame(); + +  rccs->Substitute = NULL; + +  count = rccs->GlyphCount; + +  if ( ALLOC_ARRAY( rccs->Substitute, count, +		    HB_UShort ) ) +    goto Fail2; + +  sub = rccs->Substitute; + +  if ( ACCESS_Frame( count * 2L ) ) +    goto Fail1; + +  for ( n = 0; n < count; n++ ) +    sub[n] = GET_UShort(); + +  FORGET_Frame(); + +  return HB_Err_Ok; + +Fail1: +  FREE( sub ); + +Fail2: +  for ( m = 0; m < nl; m++ ) +    _HB_OPEN_Free_Coverage( &l[m] ); + +  FREE( l ); + +Fail3: +  for ( m = 0; m < nb; m++ ) +    _HB_OPEN_Free_Coverage( &b[m] ); + +  FREE( b ); + +Fail4: +  _HB_OPEN_Free_Coverage( &rccs->Coverage ); +  return error; +} + + +static void  Free_ReverseChainContextSubst( HB_GSUB_SubTable* st ) +{ +  HB_UShort      n, count; +  HB_ReverseChainContextSubst*  rccs = &st->reverse; + +  HB_Coverage*  c; + +  _HB_OPEN_Free_Coverage( &rccs->Coverage ); + +  if ( rccs->LookaheadCoverage ) +  { +    count = rccs->LookaheadGlyphCount; +    c     = rccs->LookaheadCoverage; + +    for ( n = 0; n < count; n++ ) +      _HB_OPEN_Free_Coverage( &c[n] ); + +    FREE( c ); +  } + +  if ( rccs->BacktrackCoverage ) +  { +    count = rccs->BacktrackGlyphCount; +    c     = rccs->BacktrackCoverage; + +    for ( n = 0; n < count; n++ ) +      _HB_OPEN_Free_Coverage( &c[n] ); + +    FREE( c ); +  } + +  FREE ( rccs->Substitute ); +} + + +static HB_Error  Lookup_ReverseChainContextSubst( HB_GSUBHeader*    gsub, +						  HB_GSUB_SubTable* st, +						  HB_Buffer         buffer, +						  HB_UShort          flags, +						  HB_UShort         context_length, +						  int               nesting_level ) +{ +  HB_UShort        index, input_index, i, j, property; +  HB_UShort        bgc, lgc; +  HB_Error         error; + +  HB_ReverseChainContextSubst*  rccs = &st->reverse; +  HB_Coverage*    bc; +  HB_Coverage*    lc; +  HB_GDEFHeader*  gdef; + +  if ( nesting_level != 1 || context_length != 0xFFFF ) +    return HB_Err_Not_Covered; + +  gdef = gsub->gdef; + +  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) ) +    return error; + +  bgc = rccs->BacktrackGlyphCount; +  lgc = rccs->LookaheadGlyphCount; + +  /* check whether context is too long; it is a first guess only */ + +  if ( bgc > buffer->in_pos || buffer->in_pos + 1 + lgc > buffer->in_length ) +    return HB_Err_Not_Covered; + +  if ( bgc ) +  { +    /* Since we don't know in advance the number of glyphs to inspect, +       we search backwards for matches in the backtrack glyph array    */ + +    bc       = rccs->BacktrackCoverage; + +    for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- ) +    { +      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +      { +	if ( error && error != HB_Err_Not_Covered ) +	  return error; + +	if ( j + 1 == bgc - i ) +	  return HB_Err_Not_Covered; +	j--; +      } + +      error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index ); +      if ( error ) +	return error; +    } +  } + +  j = buffer->in_pos; + +  error = _HB_OPEN_Coverage_Index( &rccs->Coverage, IN_GLYPH( j ), &input_index ); +  if ( error ) +      return error; + +  lc       = rccs->LookaheadCoverage; + +  for ( i = 0, j = buffer->in_pos + 1; i < lgc; i++, j++ ) +  { +    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) ) +    { +      if ( error && error != HB_Err_Not_Covered ) +	return error; + +      if ( j + lgc - i == (HB_Int)buffer->in_length ) +	return HB_Err_Not_Covered; +      j++; +    } + +    error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index ); +    if ( error ) +      return error; +  } + +  IN_CURGLYPH() = rccs->Substitute[input_index]; +  buffer->in_pos--; /* Reverse! */ + +  return error; +} + + + +/*********** + * GSUB API + ***********/ + + + +HB_Error  HB_GSUB_Select_Script( HB_GSUBHeader*  gsub, +				 HB_UInt         script_tag, +				 HB_UShort*       script_index ) +{ +  HB_UShort          n; + +  HB_ScriptList*    sl; +  HB_ScriptRecord*  sr; + + +  if ( !gsub || !script_index ) +    return ERR(HB_Err_Invalid_Argument); + +  sl = &gsub->ScriptList; +  sr = sl->ScriptRecord; + +  for ( n = 0; n < sl->ScriptCount; n++ ) +    if ( script_tag == sr[n].ScriptTag ) +    { +      *script_index = n; + +      return HB_Err_Ok; +    } + +  return HB_Err_Not_Covered; +} + + + +HB_Error  HB_GSUB_Select_Language( HB_GSUBHeader*  gsub, +				   HB_UInt         language_tag, +				   HB_UShort        script_index, +				   HB_UShort*       language_index, +				   HB_UShort*       req_feature_index ) +{ +  HB_UShort           n; + +  HB_ScriptList*     sl; +  HB_ScriptRecord*   sr; +  HB_ScriptTable*    s; +  HB_LangSysRecord*  lsr; + + +  if ( !gsub || !language_index || !req_feature_index ) +    return ERR(HB_Err_Invalid_Argument); + +  sl = &gsub->ScriptList; +  sr = sl->ScriptRecord; + +  if ( script_index >= sl->ScriptCount ) +    return ERR(HB_Err_Invalid_Argument); + +  s   = &sr[script_index].Script; +  lsr = s->LangSysRecord; + +  for ( n = 0; n < s->LangSysCount; n++ ) +    if ( language_tag == lsr[n].LangSysTag ) +    { +      *language_index = n; +      *req_feature_index = lsr[n].LangSys.ReqFeatureIndex; + +      return HB_Err_Ok; +    } + +  return HB_Err_Not_Covered; +} + + +/* selecting 0xFFFF for language_index asks for the values of the +   default language (DefaultLangSys)                              */ + + +HB_Error  HB_GSUB_Select_Feature( HB_GSUBHeader*  gsub, +				  HB_UInt         feature_tag, +				  HB_UShort        script_index, +				  HB_UShort        language_index, +				  HB_UShort*       feature_index ) +{ +  HB_UShort           n; + +  HB_ScriptList*     sl; +  HB_ScriptRecord*   sr; +  HB_ScriptTable*    s; +  HB_LangSysRecord*  lsr; +  HB_LangSys*        ls; +  HB_UShort*          fi; + +  HB_FeatureList*    fl; +  HB_FeatureRecord*  fr; + + +  if ( !gsub || !feature_index ) +    return ERR(HB_Err_Invalid_Argument); + +  sl = &gsub->ScriptList; +  sr = sl->ScriptRecord; + +  fl = &gsub->FeatureList; +  fr = fl->FeatureRecord; + +  if ( script_index >= sl->ScriptCount ) +    return ERR(HB_Err_Invalid_Argument); + +  s   = &sr[script_index].Script; +  lsr = s->LangSysRecord; + +  if ( language_index == 0xFFFF ) +    ls = &s->DefaultLangSys; +  else +  { +    if ( language_index >= s->LangSysCount ) +      return ERR(HB_Err_Invalid_Argument); + +    ls = &lsr[language_index].LangSys; +  } + +  fi = ls->FeatureIndex; + +  for ( n = 0; n < ls->FeatureCount; n++ ) +  { +    if ( fi[n] >= fl->FeatureCount ) +      return ERR(HB_Err_Invalid_SubTable_Format); + +    if ( feature_tag == fr[fi[n]].FeatureTag ) +    { +      *feature_index = fi[n]; + +      return HB_Err_Ok; +    } +  } + +  return HB_Err_Not_Covered; +} + + +/* The next three functions return a null-terminated list */ + + +HB_Error  HB_GSUB_Query_Scripts( HB_GSUBHeader*  gsub, +				 HB_UInt**       script_tag_list ) +{ +  HB_UShort          n; +  HB_Error           error; +  HB_UInt*          stl; + +  HB_ScriptList*    sl; +  HB_ScriptRecord*  sr; + + +  if ( !gsub || !script_tag_list ) +    return ERR(HB_Err_Invalid_Argument); + +  sl = &gsub->ScriptList; +  sr = sl->ScriptRecord; + +  if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) ) +    return error; + +  for ( n = 0; n < sl->ScriptCount; n++ ) +    stl[n] = sr[n].ScriptTag; +  stl[n] = 0; + +  *script_tag_list = stl; + +  return HB_Err_Ok; +} + + + +HB_Error  HB_GSUB_Query_Languages( HB_GSUBHeader*  gsub, +				   HB_UShort        script_index, +				   HB_UInt**       language_tag_list ) +{ +  HB_UShort           n; +  HB_Error            error; +  HB_UInt*           ltl; + +  HB_ScriptList*     sl; +  HB_ScriptRecord*   sr; +  HB_ScriptTable*    s; +  HB_LangSysRecord*  lsr; + + +  if ( !gsub || !language_tag_list ) +    return ERR(HB_Err_Invalid_Argument); + +  sl = &gsub->ScriptList; +  sr = sl->ScriptRecord; + +  if ( script_index >= sl->ScriptCount ) +    return ERR(HB_Err_Invalid_Argument); + +  s   = &sr[script_index].Script; +  lsr = s->LangSysRecord; + +  if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) ) +    return error; + +  for ( n = 0; n < s->LangSysCount; n++ ) +    ltl[n] = lsr[n].LangSysTag; +  ltl[n] = 0; + +  *language_tag_list = ltl; + +  return HB_Err_Ok; +} + + +/* selecting 0xFFFF for language_index asks for the values of the +   default language (DefaultLangSys)                              */ + + +HB_Error  HB_GSUB_Query_Features( HB_GSUBHeader*  gsub, +				  HB_UShort        script_index, +				  HB_UShort        language_index, +				  HB_UInt**       feature_tag_list ) +{ +  HB_UShort           n; +  HB_Error            error; +  HB_UInt*           ftl; + +  HB_ScriptList*     sl; +  HB_ScriptRecord*   sr; +  HB_ScriptTable*    s; +  HB_LangSysRecord*  lsr; +  HB_LangSys*        ls; +  HB_UShort*          fi; + +  HB_FeatureList*    fl; +  HB_FeatureRecord*  fr; + + +  if ( !gsub || !feature_tag_list ) +    return ERR(HB_Err_Invalid_Argument); + +  sl = &gsub->ScriptList; +  sr = sl->ScriptRecord; + +  fl = &gsub->FeatureList; +  fr = fl->FeatureRecord; + +  if ( script_index >= sl->ScriptCount ) +    return ERR(HB_Err_Invalid_Argument); + +  s   = &sr[script_index].Script; +  lsr = s->LangSysRecord; + +  if ( language_index == 0xFFFF ) +    ls = &s->DefaultLangSys; +  else +  { +    if ( language_index >= s->LangSysCount ) +      return ERR(HB_Err_Invalid_Argument); + +    ls = &lsr[language_index].LangSys; +  } + +  fi = ls->FeatureIndex; + +  if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) ) +    return error; + +  for ( n = 0; n < ls->FeatureCount; n++ ) +  { +    if ( fi[n] >= fl->FeatureCount ) +    { +      FREE( ftl ); +      return ERR(HB_Err_Invalid_SubTable_Format); +    } +    ftl[n] = fr[fi[n]].FeatureTag; +  } +  ftl[n] = 0; + +  *feature_tag_list = ftl; + +  return HB_Err_Ok; +} + + +/* Do an individual subtable lookup.  Returns HB_Err_Ok if substitution +   has been done, or HB_Err_Not_Covered if not.                        */ +static HB_Error  GSUB_Do_Glyph_Lookup( HB_GSUBHeader* gsub, +				       HB_UShort       lookup_index, +				       HB_Buffer      buffer, +				       HB_UShort       context_length, +				       int             nesting_level ) +{ +  HB_Error               error = HB_Err_Not_Covered; +  HB_UShort              i, flags, lookup_count; +  HB_Lookup*             lo; +  int                    lookup_type; + +  nesting_level++; + +  if ( nesting_level > HB_MAX_NESTING_LEVEL ) +    return ERR(HB_Err_Not_Covered); /* ERR() call intended */ + +  lookup_count = gsub->LookupList.LookupCount; +  if (lookup_index >= lookup_count) +    return error; + +  lo    = &gsub->LookupList.Lookup[lookup_index]; +  flags = lo->LookupFlag; +  lookup_type = lo->LookupType; + +  for ( i = 0; i < lo->SubTableCount; i++ ) +  { +    HB_GSUB_SubTable *st = &lo->SubTable[i].st.gsub; + +    switch (lookup_type) { +      case HB_GSUB_LOOKUP_SINGLE: +	error = Lookup_SingleSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break; +      case HB_GSUB_LOOKUP_MULTIPLE: +	error = Lookup_MultipleSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break; +      case HB_GSUB_LOOKUP_ALTERNATE: +	error = Lookup_AlternateSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break; +      case HB_GSUB_LOOKUP_LIGATURE: +	error = Lookup_LigatureSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break; +      case HB_GSUB_LOOKUP_CONTEXT: +	error = Lookup_ContextSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break; +      case HB_GSUB_LOOKUP_CHAIN: +	error = Lookup_ChainContextSubst	( gsub, st, buffer, flags, context_length, nesting_level ); break; +    /*case HB_GSUB_LOOKUP_EXTENSION: +	error = Lookup_ExtensionSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;*/ +      case HB_GSUB_LOOKUP_REVERSE_CHAIN: +	error = Lookup_ReverseChainContextSubst	( gsub, st, buffer, flags, context_length, nesting_level ); break; +      default: +	error = HB_Err_Not_Covered; +    }; + +    /* Check whether we have a successful substitution or an error other +       than HB_Err_Not_Covered                                          */ +    if ( error != HB_Err_Not_Covered ) +      return error; +  } + +  return HB_Err_Not_Covered; +} + + +HB_INTERNAL HB_Error +_HB_GSUB_Load_SubTable( HB_GSUB_SubTable* st, +			HB_Stream         stream, +			HB_UShort         lookup_type ) +{ +  switch (lookup_type) { +    case HB_GSUB_LOOKUP_SINGLE:		return Load_SingleSubst			( st, stream ); +    case HB_GSUB_LOOKUP_MULTIPLE:	return Load_MultipleSubst		( st, stream ); +    case HB_GSUB_LOOKUP_ALTERNATE:	return Load_AlternateSubst		( st, stream ); +    case HB_GSUB_LOOKUP_LIGATURE:	return Load_LigatureSubst		( st, stream ); +    case HB_GSUB_LOOKUP_CONTEXT:	return Load_ContextSubst		( st, stream ); +    case HB_GSUB_LOOKUP_CHAIN:		return Load_ChainContextSubst		( st, stream ); +  /*case HB_GSUB_LOOKUP_EXTENSION:	return Load_ExtensionSubst		( st, stream );*/ +    case HB_GSUB_LOOKUP_REVERSE_CHAIN:	return Load_ReverseChainContextSubst	( st, stream ); +    default:				return ERR(HB_Err_Invalid_SubTable_Format); +  }; +} + + +HB_INTERNAL void +_HB_GSUB_Free_SubTable( HB_GSUB_SubTable* st, +			HB_UShort         lookup_type ) +{ +  switch ( lookup_type ) { +    case HB_GSUB_LOOKUP_SINGLE:		Free_SingleSubst		( st ); return; +    case HB_GSUB_LOOKUP_MULTIPLE:	Free_MultipleSubst		( st ); return; +    case HB_GSUB_LOOKUP_ALTERNATE:	Free_AlternateSubst		( st ); return; +    case HB_GSUB_LOOKUP_LIGATURE:	Free_LigatureSubst		( st ); return; +    case HB_GSUB_LOOKUP_CONTEXT:	Free_ContextSubst		( st ); return; +    case HB_GSUB_LOOKUP_CHAIN:		Free_ChainContextSubst		( st ); return; +  /*case HB_GSUB_LOOKUP_EXTENSION:	Free_ExtensionSubst		( st ); return;*/ +    case HB_GSUB_LOOKUP_REVERSE_CHAIN:	Free_ReverseChainContextSubst	( st ); return; +    default:									return; +  }; +} + + + +/* apply one lookup to the input string object */ + +static HB_Error  GSUB_Do_String_Lookup( HB_GSUBHeader*   gsub, +				   HB_UShort         lookup_index, +				   HB_Buffer        buffer ) +{ +  HB_Error  error, retError = HB_Err_Not_Covered; + +  HB_UInt*  properties = gsub->LookupList.Properties; +  int       lookup_type = gsub->LookupList.Lookup[lookup_index].LookupType; + +  const int       nesting_level = 0; +  /* 0xFFFF indicates that we don't have a context length yet */ +  const HB_UShort context_length = 0xFFFF; + +  switch (lookup_type) { + +    case HB_GSUB_LOOKUP_SINGLE: +    case HB_GSUB_LOOKUP_MULTIPLE: +    case HB_GSUB_LOOKUP_ALTERNATE: +    case HB_GSUB_LOOKUP_LIGATURE: +    case HB_GSUB_LOOKUP_CONTEXT: +    case HB_GSUB_LOOKUP_CHAIN: +      /* in/out forward substitution (implemented lazy) */ + +      _hb_buffer_clear_output ( buffer ); +      buffer->in_pos = 0; +  while ( buffer->in_pos < buffer->in_length ) +  { +    if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] ) +    { +	  error = GSUB_Do_Glyph_Lookup( gsub, lookup_index, buffer, context_length, nesting_level ); +      if ( error ) +      { +	if ( error != HB_Err_Not_Covered ) +	  return error; +      } +      else +	retError = error; +    } +    else +      error = HB_Err_Not_Covered; + +    if ( error == HB_Err_Not_Covered ) +	  if ( COPY_Glyph ( buffer ) ) +	return error; +  } +      /* we shouldn't swap if error occurred. +       * +       * also don't swap if nothing changed (ie HB_Err_Not_Covered). +       * shouldn't matter in that case though. +       */ +      if ( retError == HB_Err_Ok ) +	_hb_buffer_swap( buffer ); + +  return retError; + +    case HB_GSUB_LOOKUP_REVERSE_CHAIN: +      /* in-place backward substitution */ + +      buffer->in_pos = buffer->in_length - 1; +    do +    { +      if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] ) +	{ +	  error = GSUB_Do_Glyph_Lookup( gsub, lookup_index, buffer, context_length, nesting_level ); +	  if ( error ) +	    { +	      if ( error != HB_Err_Not_Covered ) +		return error; +	    } +	  else +	    retError = error; +	} +	else +	  error = HB_Err_Not_Covered; + +	if ( error == HB_Err_Not_Covered ) +	  buffer->in_pos--; +      } +      while ((HB_Int) buffer->in_pos >= 0); + +      return retError; + +  /*case HB_GSUB_LOOKUP_EXTENSION:*/ +    default: +  return retError; +  }; +} + + +HB_Error  HB_GSUB_Add_Feature( HB_GSUBHeader*  gsub, +			       HB_UShort        feature_index, +			       HB_UInt          property ) +{ +  HB_UShort    i; + +  HB_Feature  feature; +  HB_UInt*     properties; +  HB_UShort*   index; +  HB_UShort    lookup_count; + +  /* Each feature can only be added once */ + +  if ( !gsub || +       feature_index >= gsub->FeatureList.FeatureCount || +       gsub->FeatureList.ApplyCount == gsub->FeatureList.FeatureCount ) +    return ERR(HB_Err_Invalid_Argument); + +  gsub->FeatureList.ApplyOrder[gsub->FeatureList.ApplyCount++] = feature_index; + +  properties = gsub->LookupList.Properties; + +  feature = gsub->FeatureList.FeatureRecord[feature_index].Feature; +  index   = feature.LookupListIndex; +  lookup_count = gsub->LookupList.LookupCount; + +  for ( i = 0; i < feature.LookupListCount; i++ ) +  { +    HB_UShort lookup_index = index[i]; +    if (lookup_index < lookup_count) +      properties[lookup_index] |= property; +  } + +  return HB_Err_Ok; +} + + + +HB_Error  HB_GSUB_Clear_Features( HB_GSUBHeader*  gsub ) +{ +  HB_UShort i; + +  HB_UInt*  properties; + + +  if ( !gsub ) +    return ERR(HB_Err_Invalid_Argument); + +  gsub->FeatureList.ApplyCount = 0; + +  properties = gsub->LookupList.Properties; + +  for ( i = 0; i < gsub->LookupList.LookupCount; i++ ) +    properties[i] = 0; + +  return HB_Err_Ok; +} + + + +HB_Error  HB_GSUB_Register_Alternate_Function( HB_GSUBHeader*  gsub, +					       HB_AltFunction  altfunc, +					       void*            data ) +{ +  if ( !gsub ) +    return ERR(HB_Err_Invalid_Argument); + +  gsub->altfunc = altfunc; +  gsub->data    = data; + +  return HB_Err_Ok; +} + +/* returns error if one happened, otherwise returns HB_Err_Not_Covered if no + * feature were applied, or HB_Err_Ok otherwise. + */ +HB_Error  HB_GSUB_Apply_String( HB_GSUBHeader*   gsub, +				HB_Buffer        buffer ) +{ +  HB_Error          error, retError = HB_Err_Not_Covered; +  int               i, j, lookup_count, num_features; + +  if ( !gsub || +       !buffer) +    return ERR(HB_Err_Invalid_Argument); + +  if ( buffer->in_length == 0 ) +    return retError; + +  lookup_count = gsub->LookupList.LookupCount; +  num_features = gsub->FeatureList.ApplyCount; + +  for ( i = 0; i < num_features; i++) +  { +    HB_UShort  feature_index = gsub->FeatureList.ApplyOrder[i]; +    HB_Feature feature = gsub->FeatureList.FeatureRecord[feature_index].Feature; + +    for ( j = 0; j < feature.LookupListCount; j++ ) +    { +      HB_UShort         lookup_index = feature.LookupListIndex[j]; + +      /* Skip nonexistant lookups */ +      if (lookup_index >= lookup_count) +       continue; + +	error = GSUB_Do_String_Lookup( gsub, lookup_index, buffer ); +      if ( error ) +      { +	if ( error != HB_Err_Not_Covered ) +	  return error; +      } +      else +	retError = error; +    } +  } + +  error = retError; + +  return error; +} + + +/* END */ | 
