Commit message: None
CVE: CVE-2011-3193
CWE: CWE-119
Vulnerability description: Heap-based buffer overflow in the Lookup_MarkMarkPos function in the HarfBuzz module (harfbuzz-gpos.c), as used by Qt before 4.7.4 and Pango, allows remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via a crafted font file.

Project: harfbuzz

static HB_Error  Do_ContextPos( GPOS_Instance*        gpi,
				HB_UShort             GlyphCount,
				HB_UShort             PosCount,
				HB_PosLookupRecord*  pos,
				HB_Buffer            buffer,
				int                   nesting_level )
{
  HB_Error  error;
  HB_UInt   i, old_pos;


  i = 0;

  while ( i < GlyphCount )
  {
    if ( PosCount && i == pos->SequenceIndex )
    {
      old_pos = buffer->in_pos;

      /* Do a positioning */

      error = GPOS_Do_Glyph_Lookup( gpi, pos->LookupListIndex, buffer,
				    GlyphCount, nesting_level );

      if ( error )
	return error;

      pos++;
      PosCount--;
      i += buffer->in_pos - old_pos;
    }
    else
    {
      i++;
      (buffer->in_pos)++;
    }
  }

  return HB_Err_Ok;
}

Project: harfbuzz

static void  Free_ChainContextPos( HB_GPOS_SubTable* st )
{
  HB_ChainContextPos*  ccp = &st->chain;

  switch ( ccp->PosFormat )
  {
  case 1:  Free_ChainContextPos1( &ccp->ccpf.ccpf1 ); break;
  case 2:  Free_ChainContextPos2( &ccp->ccpf.ccpf2 ); break;
  case 3:  Free_ChainContextPos3( &ccp->ccpf.ccpf3 ); break;
  default:						      break;
  }
}

Project: harfbuzz

static void  Free_ChainContextPos1( HB_ChainContextPosFormat1*  ccpf1 )
{
  HB_UShort             n, count;

  HB_ChainPosRuleSet*  cprs;


  if ( ccpf1->ChainPosRuleSet )
  {
    count = ccpf1->ChainPosRuleSetCount;
    cprs  = ccpf1->ChainPosRuleSet;

    for ( n = 0; n < count; n++ )
      Free_ChainPosRuleSet( &cprs[n] );

    FREE( cprs );
  }

  _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
}

Project: harfbuzz

static void  Free_ChainContextPos3( HB_ChainContextPosFormat3*  ccpf3 )
{
  HB_UShort      n, count;

  HB_Coverage*  c;


  FREE( ccpf3->PosLookupRecord );

  if ( ccpf3->LookaheadCoverage )
  {
    count = ccpf3->LookaheadGlyphCount;
    c     = ccpf3->LookaheadCoverage;

    for ( n = 0; n < count; n++ )
      _HB_OPEN_Free_Coverage( &c[n] );

    FREE( c );
  }

  if ( ccpf3->InputCoverage )
  {
    count = ccpf3->InputGlyphCount;
    c     = ccpf3->InputCoverage;

    for ( n = 0; n < count; n++ )
      _HB_OPEN_Free_Coverage( &c[n] );

    FREE( c );
  }

  if ( ccpf3->BacktrackCoverage )
  {
    count = ccpf3->BacktrackGlyphCount;
    c     = ccpf3->BacktrackCoverage;

    for ( n = 0; n < count; n++ )
      _HB_OPEN_Free_Coverage( &c[n] );

    FREE( c );
  }
}

Project: harfbuzz

static void  Free_ChainPosClassRule( HB_ChainPosClassRule*  cpcr )
{
  FREE( cpcr->PosLookupRecord );
  FREE( cpcr->Lookahead );
  FREE( cpcr->Input );
  FREE( cpcr->Backtrack );
}

Project: harfbuzz

static void  Free_ChainPosRuleSet( HB_ChainPosRuleSet*  cprs )
{
  HB_UShort          n, count;

  HB_ChainPosRule*  cpr;


  if ( cprs->ChainPosRule )
  {
    count = cprs->ChainPosRuleCount;
    cpr   = cprs->ChainPosRule;

    for ( n = 0; n < count; n++ )
      Free_ChainPosRule( &cpr[n] );

    FREE( cpr );
  }
}

Project: harfbuzz

static void  Free_ContextPos( HB_GPOS_SubTable* st )
{
  HB_ContextPos*   cp = &st->context;

  switch ( cp->PosFormat )
  {
  case 1:  Free_ContextPos1( &cp->cpf.cpf1 ); break;
  case 2:  Free_ContextPos2( &cp->cpf.cpf2 ); break;
  case 3:  Free_ContextPos3( &cp->cpf.cpf3 ); break;
  default:					      break;
  }
}

Project: harfbuzz

static void  Free_ContextPos1( HB_ContextPosFormat1*  cpf1 )
{
  HB_UShort        n, count;

  HB_PosRuleSet*  prs;


  if ( cpf1->PosRuleSet )
  {
    count = cpf1->PosRuleSetCount;
    prs   = cpf1->PosRuleSet;

    for ( n = 0; n < count; n++ )
      Free_PosRuleSet( &prs[n] );

    FREE( prs );
  }

  _HB_OPEN_Free_Coverage( &cpf1->Coverage );
}

Project: harfbuzz

static void  Free_ContextPos2( HB_ContextPosFormat2*  cpf2 )
{
  HB_UShort         n, count;

  HB_PosClassSet*  pcs;


  if ( cpf2->PosClassSet )
  {
    count = cpf2->PosClassSetCount;
    pcs   = cpf2->PosClassSet;

    for ( n = 0; n < count; n++ )
      Free_PosClassSet( &pcs[n] );

    FREE( pcs );
  }

  _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
  _HB_OPEN_Free_Coverage( &cpf2->Coverage );
}

Project: harfbuzz

static void  Free_CursivePos( HB_GPOS_SubTable* st )
{
  HB_UShort             n, count;
  HB_CursivePos*  cp = &st->cursive;

  HB_EntryExitRecord*  eer;


  if ( cp->EntryExitRecord )
  {
    count = cp->EntryExitCount;
    eer   = cp->EntryExitRecord;

    for ( n = 0; n < count; n++ )
    {
      Free_Anchor( &eer[n].EntryAnchor );
      Free_Anchor( &eer[n].ExitAnchor );
    }

    FREE( eer );
  }

  _HB_OPEN_Free_Coverage( &cp->Coverage );
}

Project: harfbuzz

static void  Free_LigatureAttach( HB_LigatureAttach*  lat,
				  HB_UShort            num_classes )
{
  HB_UShort        m, n, count;

  HB_ComponentRecord*  cr;
  HB_Anchor*           lan;


  if ( lat->ComponentRecord )
  {
    count = lat->ComponentCount;
    cr    = lat->ComponentRecord;

    for ( m = 0; m < count; m++ )
    {
      lan = cr[m].LigatureAnchor;

      for ( n = 0; n < num_classes; n++ )
	Free_Anchor( &lan[n] );

      FREE( lan );
    }

    FREE( cr );
  }
}

Project: harfbuzz

static void  Free_Mark2Array( HB_Mark2Array*  m2a,
			      HB_UShort        num_classes )
{
  HB_Mark2Record  *m2r;
  HB_Anchor       *m2ans;

  HB_UNUSED(num_classes);

  if ( m2a->Mark2Record )
  {
    m2r   = m2a->Mark2Record;

    if ( m2a->Mark2Count )
    {
      m2ans = m2r[0].Mark2Anchor;
      FREE( m2ans );
    }

    FREE( m2r );
  }
}

Project: harfbuzz

static void  Free_MarkArray( HB_MarkArray*  ma )
{
  HB_UShort        n, count;

  HB_MarkRecord*  mr;


  if ( ma->MarkRecord )
  {
    count = ma->MarkCount;
    mr    = ma->MarkRecord;

    for ( n = 0; n < count; n++ )
      Free_Anchor( &mr[n].MarkAnchor );

    FREE( mr );
  }
}

Project: harfbuzz

static void  Free_MarkBasePos( HB_GPOS_SubTable* st )
{
  HB_MarkBasePos* mbp = &st->markbase;

  Free_BaseArray( &mbp->BaseArray, mbp->ClassCount );
  Free_MarkArray( &mbp->MarkArray );
  _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
  _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
}

Project: harfbuzz

static void  Free_MarkMarkPos( HB_GPOS_SubTable* st)
{
  HB_MarkMarkPos* mmp = &st->markmark;

  Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount );
  Free_MarkArray( &mmp->Mark1Array );
  _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
  _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
}

Project: harfbuzz

static void  Free_PairPos( HB_GPOS_SubTable* st )
{
  HB_UShort  format1, format2;
  HB_PairPos*     pp = &st->pair;


  format1 = pp->ValueFormat1;
  format2 = pp->ValueFormat2;

  switch ( pp->PosFormat )
  {
  case 1:
    Free_PairPos1( &pp->ppf.ppf1, format1, format2 );
    break;

  case 2:
    Free_PairPos2( &pp->ppf.ppf2, format1, format2 );
    break;

  default:
    break;
  }

  _HB_OPEN_Free_Coverage( &pp->Coverage );
}

Project: harfbuzz

static void  Free_PairPos1( HB_PairPosFormat1*  ppf1,
			    HB_UShort            format1,
			    HB_UShort            format2 )
{
  HB_UShort     n, count;

  HB_PairSet*  ps;


  if ( ppf1->PairSet )
  {
    count = ppf1->PairSetCount;
    ps    = ppf1->PairSet;

    for ( n = 0; n < count; n++ )
      Free_PairSet( &ps[n], format1, format2 );

    FREE( ps );
  }
}

Project: harfbuzz

static void  Free_PairPos2( HB_PairPosFormat2*  ppf2,
			    HB_UShort            format1,
			    HB_UShort            format2)
{
  HB_UShort          m, n, count1, count2;

  HB_Class1Record*  c1r;
  HB_Class2Record*  c2r;


  if ( ppf2->Class1Record )
  {
    c1r    = ppf2->Class1Record;
    count1 = ppf2->Class1Count;
    count2 = ppf2->Class2Count;

    for ( m = 0; m < count1; m++ )
    {
      c2r = c1r[m].Class2Record;

      for ( n = 0; n < count2; n++ )
      {
	if ( format1 )
	  Free_ValueRecord( &c2r[n].Value1, format1 );
	if ( format2 )
	  Free_ValueRecord( &c2r[n].Value2, format2 );
      }

      FREE( c2r );
    }

    FREE( c1r );

    _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
    _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
  }
}

Project: harfbuzz

static void  Free_PairSet( HB_PairSet*  ps,
			   HB_UShort     format1,
			   HB_UShort     format2 )
{
  HB_UShort             n, count;

  HB_PairValueRecord*  pvr;


  if ( ps->PairValueRecord )
  {
    count = ps->PairValueCount;
    pvr   = ps->PairValueRecord;

    for ( n = 0; n < count; n++ )
    {
      if ( format1 )
	Free_ValueRecord( &pvr[n].Value1, format1 );
      if ( format2 )
	Free_ValueRecord( &pvr[n].Value2, format2 );
    }

    FREE( pvr );
  }
}

Project: harfbuzz

static void  Free_PosClassSet( HB_PosClassSet*  pcs )
{
  HB_UShort          n, count;

  HB_PosClassRule*  pcr;


  if ( pcs->PosClassRule )
  {
    count = pcs->PosClassRuleCount;
    pcr   = pcs->PosClassRule;

    for ( n = 0; n < count; n++ )
      Free_PosClassRule( &pcr[n] );

    FREE( pcr );
  }
}

Project: harfbuzz

static void  Free_PosRule( HB_PosRule*  pr )
{
  FREE( pr->PosLookupRecord );
  FREE( pr->Input );
}

Project: harfbuzz

static void  Free_SinglePos( HB_GPOS_SubTable* st )
{
  HB_UShort         n, count, format;
  HB_SinglePos*   sp = &st->single;

  HB_ValueRecord*  v;


  format = sp->ValueFormat;

  switch ( sp->PosFormat )
  {
  case 1:
    Free_ValueRecord( &sp->spf.spf1.Value, format );
    break;

  case 2:
    if ( sp->spf.spf2.Value )
    {
      count = sp->spf.spf2.ValueCount;
      v     = sp->spf.spf2.Value;

      for ( n = 0; n < count; n++ )
	Free_ValueRecord( &v[n], format );

      FREE( v );
    }
    break;
  default:
    break;
  }

  _HB_OPEN_Free_Coverage( &sp->Coverage );
}

Project: harfbuzz

static void  Free_ValueRecord( HB_ValueRecord*  vr,
			       HB_UShort         format )
{
  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_ADVANCE_DEVICE] );
  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_X_ADVANCE_DEVICE] );
  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_PLACEMENT_DEVICE] );
  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_X_PLACEMENT_DEVICE] );
  FREE( vr->DeviceTables );
}

Project: harfbuzz

static HB_Error  GPOS_Do_Glyph_Lookup( GPOS_Instance*    gpi,
				       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_GPOSHeader*       gpos = gpi->gpos;
  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 = gpos->LookupList.LookupCount;
  if (lookup_index >= lookup_count)
    return error;

  lo    = &gpos->LookupList.Lookup[lookup_index];
  flags = lo->LookupFlag;
  lookup_type = lo->LookupType;

  for ( i = 0; i < lo->SubTableCount; i++ )
  {
    HB_GPOS_SubTable *st = &lo->SubTable[i].st.gpos;

    switch (lookup_type) {
      case HB_GPOS_LOOKUP_SINGLE:
        error = Lookup_SinglePos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_PAIR:
	error = Lookup_PairPos		( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_CURSIVE:
	error = Lookup_CursivePos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_MARKBASE:
	error = Lookup_MarkBasePos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_MARKLIG:
	error = Lookup_MarkLigPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_MARKMARK:
	error = Lookup_MarkMarkPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_CONTEXT:
	error = Lookup_ContextPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
      case HB_GPOS_LOOKUP_CHAIN:
	error = Lookup_ChainContextPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
    /*case HB_GPOS_LOOKUP_EXTENSION:
	error = Lookup_ExtensionPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;*/
      default:
	error = HB_Err_Not_Covered;
    }

    /* Check whether we have a successful positioning or an error other
       than HB_Err_Not_Covered                                         */
    if ( error != HB_Err_Not_Covered )
      return error;
  }

  return HB_Err_Not_Covered;
}

Project: harfbuzz

static HB_Error  GPOS_Do_String_Lookup( GPOS_Instance*    gpi,
				   HB_UShort         lookup_index,
				   HB_Buffer        buffer )
{
  HB_Error         error, retError = HB_Err_Not_Covered;
  HB_GPOSHeader*  gpos = gpi->gpos;

  HB_UInt*  properties = gpos->LookupList.Properties;

  const int       nesting_level = 0;
  /* 0xFFFF indicates that we don't have a context length yet */
  const HB_UShort context_length = 0xFFFF;


  gpi->last  = 0xFFFF;     /* no last valid glyph for cursive pos. */

  buffer->in_pos = 0;
  while ( buffer->in_pos < buffer->in_length )
  {
    if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
    {
      /* Note that the connection between mark and base glyphs hold
	 exactly one (string) lookup.  For example, it would be possible
	 that in the first lookup, mark glyph X is attached to base
	 glyph A, and in the next lookup it is attached to base glyph B.
	 It is up to the font designer to provide meaningful lookups and
	 lookup order.                                                   */

      error = GPOS_Do_Glyph_Lookup( gpi, lookup_index, buffer, context_length, nesting_level );
      if ( error && error != HB_Err_Not_Covered )
	return error;
    }
    else
    {
      /* Contrary to properties defined in GDEF, user-defined properties
	 will always stop a possible cursive positioning.                */
      gpi->last = 0xFFFF;

      error = HB_Err_Not_Covered;
    }

    if ( error == HB_Err_Not_Covered )
      (buffer->in_pos)++;
    else
      retError = error;
  }

  return retError;
}

Project: harfbuzz

static HB_Error  Get_ValueRecord( GPOS_Instance*    gpi,
				  HB_ValueRecord*  vr,
				  HB_UShort         format,
				  HB_Position      gd )
{
  HB_Short         pixel_value;
  HB_Error         error = HB_Err_Ok;
#ifdef HB_SUPPORT_MULTIPLE_MASTER
  HB_GPOSHeader*  gpos = gpi->gpos;
  HB_Fixed           value;
#endif

  HB_UShort  x_ppem, y_ppem;
  HB_16Dot16   x_scale, y_scale;


  if ( !format )
    return HB_Err_Ok;

  x_ppem  = gpi->font->x_ppem;
  y_ppem  = gpi->font->y_ppem;
  x_scale = gpi->font->x_scale;
  y_scale = gpi->font->y_scale;

  /* design units -> fractional pixel */

  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
    gd->x_pos += x_scale * vr->XPlacement / 0x10000;
  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
    gd->y_pos += y_scale * vr->YPlacement / 0x10000;
  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
    gd->x_advance += x_scale * vr->XAdvance / 0x10000;
  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
    gd->y_advance += y_scale * vr->YAdvance / 0x10000;

  if ( !gpi->dvi )
  {
    /* pixel -> fractional pixel */

    if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
    {
      _HB_OPEN_Get_Device( vr->DeviceTables[VR_X_PLACEMENT_DEVICE], x_ppem, &pixel_value );
      gd->x_pos += pixel_value << 6;
    }
    if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
    {
      _HB_OPEN_Get_Device( vr->DeviceTables[VR_Y_PLACEMENT_DEVICE], y_ppem, &pixel_value );
      gd->y_pos += pixel_value << 6;
    }
    if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
    {
      _HB_OPEN_Get_Device( vr->DeviceTables[VR_X_ADVANCE_DEVICE], x_ppem, &pixel_value );
      gd->x_advance += pixel_value << 6;
    }
    if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
    {
      _HB_OPEN_Get_Device( vr->DeviceTables[VR_Y_ADVANCE_DEVICE], y_ppem, &pixel_value );
      gd->y_advance += pixel_value << 6;
    }
  }

#ifdef HB_SUPPORT_MULTIPLE_MASTER
  /* values returned from mmfunc() are already in fractional pixels */

  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
  {
    error = (gpos->mmfunc)( gpi->font, vr->XIdPlacement,
			    &value, gpos->data );
    if ( error )
      return error;
    gd->x_pos += value;
  }
  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
  {
    error = (gpos->mmfunc)( gpi->font, vr->YIdPlacement,
			    &value, gpos->data );
    if ( error )
      return error;
    gd->y_pos += value;
  }
  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
  {
    error = (gpos->mmfunc)( gpi->font, vr->XIdAdvance,
			    &value, gpos->data );
    if ( error )
      return error;
    gd->x_advance += value;
  }
  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
  {
    error = (gpos->mmfunc)( gpi->font, vr->YIdAdvance,
			    &value, gpos->data );
    if ( error )
      return error;
    gd->y_advance += value;
  }
#endif

  return error;
}

Project: harfbuzz

HB_Error  HB_Done_GPOS_Table( HB_GPOSHeader* gpos )
{
  _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
  _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
  _HB_OPEN_Free_ScriptList( &gpos->ScriptList );

  FREE( gpos );

  return HB_Err_Ok;
}

Project: harfbuzz

HB_Error  HB_GPOS_Add_Feature( HB_GPOSHeader*  gpos,
			       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 ( !gpos ||
       feature_index >= gpos->FeatureList.FeatureCount ||
       gpos->FeatureList.ApplyCount == gpos->FeatureList.FeatureCount )
    return ERR(HB_Err_Invalid_Argument);

  gpos->FeatureList.ApplyOrder[gpos->FeatureList.ApplyCount++] = feature_index;

  properties = gpos->LookupList.Properties;

  feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
  index   = feature.LookupListIndex;
  lookup_count = gpos->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;
}

Project: harfbuzz

HB_Error  HB_GPOS_Apply_String( HB_Font            font,
				HB_GPOSHeader*    gpos,
				HB_UShort          load_flags,
				HB_Buffer         buffer,
				HB_Bool            dvi,
				HB_Bool            r2l )
{
  HB_Error       error, retError = HB_Err_Not_Covered;
  GPOS_Instance  gpi;
  int            i, j, lookup_count, num_features;

  if ( !font || !gpos || !buffer )
    return ERR(HB_Err_Invalid_Argument);

  if ( buffer->in_length == 0 )
    return HB_Err_Not_Covered;

  gpi.font       = font;
  gpi.gpos       = gpos;
  gpi.load_flags = load_flags;
  gpi.r2l        = r2l;
  gpi.dvi        = dvi;

  lookup_count = gpos->LookupList.LookupCount;
  num_features = gpos->FeatureList.ApplyCount;

  if ( num_features )
    {
      error = _hb_buffer_clear_positions( buffer );
      if ( error )
	return error;
    }

  for ( i = 0; i < num_features; i++ )
  {
    HB_UShort  feature_index = gpos->FeatureList.ApplyOrder[i];
    HB_Feature feature = gpos->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 = GPOS_Do_String_Lookup( &gpi, lookup_index, buffer );
      if ( error )
      {
	if ( error != HB_Err_Not_Covered )
	  return error;
      }
      else
	retError = error;
    }
  }

  if ( num_features )
    {
  error = Position_CursiveChain ( buffer );
  if ( error )
    return error;
    }

  return retError;
}

Project: harfbuzz

HB_Error  HB_GPOS_Query_Languages( HB_GPOSHeader*  gpos,
				   HB_UShort        script_index,
				   HB_UInt**       language_tag_list )
{
  HB_Error            error;
  HB_UShort           n;
  HB_UInt*           ltl;

  HB_ScriptList*     sl;
  HB_ScriptRecord*   sr;
  HB_ScriptTable*    s;
  HB_LangSysRecord*  lsr;


  if ( !gpos || !language_tag_list )
    return ERR(HB_Err_Invalid_Argument);

  sl = &gpos->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;
}

Project: harfbuzz

HB_Error  HB_GPOS_Query_Scripts( HB_GPOSHeader*  gpos,
				 HB_UInt**       script_tag_list )
{
  HB_Error           error;
  HB_UShort          n;
  HB_UInt*          stl;

  HB_ScriptList*    sl;
  HB_ScriptRecord*  sr;


  if ( !gpos || !script_tag_list )
    return ERR(HB_Err_Invalid_Argument);

  sl = &gpos->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;
}

Project: harfbuzz

HB_Error  HB_GPOS_Register_MM_Function( HB_GPOSHeader*  gpos,
					HB_MMFunction   mmfunc,
					void*            data )
{
  if ( !gpos )
    return ERR(HB_Err_Invalid_Argument);

  gpos->mmfunc = mmfunc;
  gpos->data   = data;

  return HB_Err_Ok;
}

Project: harfbuzz

HB_Error  HB_GPOS_Select_Feature( HB_GPOSHeader*  gpos,
				  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 ( !gpos || !feature_index )
    return ERR(HB_Err_Invalid_Argument);

  sl = &gpos->ScriptList;
  sr = sl->ScriptRecord;

  fl = &gpos->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;
}

Project: harfbuzz

HB_Error  HB_GPOS_Select_Script( HB_GPOSHeader*  gpos,
				 HB_UInt         script_tag,
				 HB_UShort*       script_index )
{
  HB_UShort          n;

  HB_ScriptList*    sl;
  HB_ScriptRecord*  sr;


  if ( !gpos || !script_index )
    return ERR(HB_Err_Invalid_Argument);

  sl = &gpos->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;
}

Project: harfbuzz

HB_Error  HB_Load_GPOS_Table( HB_Stream stream, 
			      HB_GPOSHeader** retptr,
			      HB_GDEFHeader*  gdef,
			      HB_Stream       gdefStream )
{
  HB_UInt         cur_offset, new_offset, base_offset;

  HB_GPOSHeader*  gpos;

  HB_Error   error;


  if ( !retptr )
    return ERR(HB_Err_Invalid_Argument);

  if ( GOTO_Table( TTAG_GPOS ) )
    return error;

  base_offset = FILE_Pos();

  if ( ALLOC ( gpos, sizeof( *gpos ) ) )
    return error;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
  gpos->mmfunc = default_mmfunc;
#endif

  /* 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( &gpos->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( &gpos->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( &gpos->LookupList,
				  stream, HB_Type_GPOS ) ) != HB_Err_Ok )
    goto Fail2;

  gpos->gdef = gdef;      /* can be NULL */

  if ( ( error =  _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( gdef, gdefStream,
								     gpos->LookupList.Lookup,
								     gpos->LookupList.LookupCount ) ) )
    goto Fail1;

  *retptr = gpos;

  return HB_Err_Ok;

Fail1:
  _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );

Fail2:
  _HB_OPEN_Free_FeatureList( &gpos->FeatureList );

Fail3:
  _HB_OPEN_Free_ScriptList( &gpos->ScriptList );

Fail4:
  FREE( gpos );

  return error;
}

Project: harfbuzz

static HB_Error  Load_Anchor( HB_Anchor*  an,
			      HB_Stream    stream )
{
  HB_Error  error;

  HB_UInt cur_offset, new_offset, base_offset;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  an->PosFormat = GET_UShort();

  FORGET_Frame();

  switch ( an->PosFormat )
  {
  case 1:
    if ( ACCESS_Frame( 4L ) )
      return error;

    an->af.af1.XCoordinate = GET_Short();
    an->af.af1.YCoordinate = GET_Short();

    FORGET_Frame();
    break;

  case 2:
    if ( ACCESS_Frame( 6L ) )
      return error;

    an->af.af2.XCoordinate = GET_Short();
    an->af.af2.YCoordinate = GET_Short();
    an->af.af2.AnchorPoint = GET_UShort();

    FORGET_Frame();
    break;

  case 3:
    if ( ACCESS_Frame( 6L ) )
      return error;

    an->af.af3.XCoordinate = GET_Short();
    an->af.af3.YCoordinate = GET_Short();

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      if ( ALLOC_ARRAY( an->af.af3.DeviceTables, 2, HB_Device ) )
        return error;

      an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] = 0;
      an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE] = 0;

      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = _HB_OPEN_Load_Device( &an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE],
				  stream ) ) != HB_Err_Ok )
	goto Fail2;
      (void)FILE_Seek( cur_offset );
    }

    if ( ACCESS_Frame( 2L ) )
      goto Fail;

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      if ( !an->af.af3.DeviceTables )
      {
        if ( ALLOC_ARRAY( an->af.af3.DeviceTables, 2, HB_Device ) )
          return error;

        an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] = 0;
        an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE] = 0;
      }

      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = _HB_OPEN_Load_Device( &an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE],
				  stream ) ) != HB_Err_Ok )
	goto Fail;
      (void)FILE_Seek( cur_offset );
    }
    break;

  case 4:
    if ( ACCESS_Frame( 4L ) )
      return error;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
    an->af.af4.XIdAnchor = GET_UShort();
    an->af.af4.YIdAnchor = GET_UShort();
#else
    (void) GET_UShort();
    (void) GET_UShort();
#endif

    FORGET_Frame();
    break;

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  return HB_Err_Ok;

Fail:
  if ( an->af.af3.DeviceTables )
    _HB_OPEN_Free_Device( an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] );

Fail2:
  FREE( an->af.af3.DeviceTables );
  return error;
}

Project: harfbuzz

static HB_Error  Load_BaseArray( HB_BaseArray*  ba,
				 HB_UShort       num_classes,
				 HB_Stream       stream )
{
  HB_Error  error;

  HB_UShort       m, n, count;
  HB_UInt         cur_offset, new_offset, base_offset;

  HB_BaseRecord  *br;
  HB_Anchor      *ban, *bans;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = ba->BaseCount = GET_UShort();

  FORGET_Frame();

  ba->BaseRecord = NULL;

  if ( ALLOC_ARRAY( ba->BaseRecord, count, HB_BaseRecord ) )
    return error;

  br = ba->BaseRecord;

  bans = NULL;

  if ( ALLOC_ARRAY( bans, count * num_classes, HB_Anchor ) )
    goto Fail;

  for ( m = 0; m < count; m++ )
  {
    br[m].BaseAnchor = NULL;

    ban = br[m].BaseAnchor = bans + m * num_classes;

    for ( n = 0; n < num_classes; n++ )
    {
      if ( ACCESS_Frame( 2L ) )
	goto Fail;

      new_offset = GET_UShort() + base_offset;

      FORGET_Frame();

      if (new_offset == base_offset) {
	/* XXX
	 * Doulos SIL Regular is buggy and has zero offsets here.
	 * Skip it
	 */
	ban[n].PosFormat = 0;
	continue;
      }

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = Load_Anchor( &ban[n], stream ) ) != HB_Err_Ok )
	goto Fail;
      (void)FILE_Seek( cur_offset );
    }
  }

  return HB_Err_Ok;

Fail:
  FREE( bans );
  FREE( br );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ChainContextPos( HB_GPOS_SubTable* st,
				       HB_Stream             stream )
{
  HB_Error  error;
  HB_ChainContextPos*  ccp = &st->chain;


  if ( ACCESS_Frame( 2L ) )
    return error;

  ccp->PosFormat = GET_UShort();

  FORGET_Frame();

  switch ( ccp->PosFormat )
  {
  case 1:
    return Load_ChainContextPos1( &ccp->ccpf.ccpf1, stream );

  case 2:
    return Load_ChainContextPos2( &ccp->ccpf.ccpf2, stream );

  case 3:
    return Load_ChainContextPos3( &ccp->ccpf.ccpf3, stream );

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  return HB_Err_Ok;               /* never reached */
}

Project: harfbuzz

static HB_Error  Load_ChainContextPos1( HB_ChainContextPosFormat1*  ccpf1,
					HB_Stream                    stream )
{
  HB_Error  error;

  HB_UShort             n, m, count;
  HB_UInt              cur_offset, new_offset, base_offset;

  HB_ChainPosRuleSet*  cprs;


  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( &ccpf1->Coverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 2L ) )
    goto Fail2;

  count = ccpf1->ChainPosRuleSetCount = GET_UShort();

  FORGET_Frame();

  ccpf1->ChainPosRuleSet = NULL;

  if ( ALLOC_ARRAY( ccpf1->ChainPosRuleSet, count, HB_ChainPosRuleSet ) )
    goto Fail2;

  cprs = ccpf1->ChainPosRuleSet;

  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_ChainPosRuleSet( &cprs[n], stream ) ) != HB_Err_Ok )
      goto Fail1;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail1:
  for ( m = 0; m < n; m++ )
    Free_ChainPosRuleSet( &cprs[m] );

  FREE( cprs );

Fail2:
  _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ChainContextPos3( HB_ChainContextPosFormat3*  ccpf3,
					HB_Stream                    stream )
{
  HB_Error  error;

  HB_UShort             n, nb, ni, nl, 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_PosLookupRecord*  plr;


  base_offset = FILE_Pos() - 2L;

  if ( ACCESS_Frame( 2L ) )
    return error;

  ccpf3->BacktrackGlyphCount = GET_UShort();

  FORGET_Frame();

  ccpf3->BacktrackCoverage = NULL;

  backtrack_count = ccpf3->BacktrackGlyphCount;

  if ( ALLOC_ARRAY( ccpf3->BacktrackCoverage, backtrack_count,
		    HB_Coverage ) )
    return error;

  b = ccpf3->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;

  ccpf3->InputGlyphCount = GET_UShort();

  FORGET_Frame();

  ccpf3->InputCoverage = NULL;

  input_count = ccpf3->InputGlyphCount;

  if ( ALLOC_ARRAY( ccpf3->InputCoverage, input_count, HB_Coverage ) )
    goto Fail4;

  i = ccpf3->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;

  ccpf3->LookaheadGlyphCount = GET_UShort();

  FORGET_Frame();

  ccpf3->LookaheadCoverage = NULL;

  lookahead_count = ccpf3->LookaheadGlyphCount;

  if ( ALLOC_ARRAY( ccpf3->LookaheadCoverage, lookahead_count,
		    HB_Coverage ) )
    goto Fail3;

  l = ccpf3->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;

  ccpf3->PosCount = GET_UShort();

  FORGET_Frame();

  ccpf3->PosLookupRecord = NULL;

  count = ccpf3->PosCount;

  if ( ALLOC_ARRAY( ccpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
    goto Fail2;

  plr = ccpf3->PosLookupRecord;

  if ( ACCESS_Frame( count * 4L ) )
    goto Fail1;

  for ( n = 0; n < count; n++ )
  {
    plr[n].SequenceIndex   = GET_UShort();
    plr[n].LookupListIndex = GET_UShort();
  }

  FORGET_Frame();

  return HB_Err_Ok;

Fail1:
  FREE( plr );

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;
}

Project: harfbuzz

static HB_Error  Load_ChainPosClassRule(
		   HB_ChainContextPosFormat2*  ccpf2,
		   HB_ChainPosClassRule*       cpcr,
		   HB_Stream                    stream )
{
  HB_Error  error;

  HB_UShort             n, count;

  HB_UShort*            b;
  HB_UShort*            i;
  HB_UShort*            l;
  HB_PosLookupRecord*  plr;


  if ( ACCESS_Frame( 2L ) )
    return error;

  cpcr->BacktrackGlyphCount = GET_UShort();

  FORGET_Frame();

  if ( cpcr->BacktrackGlyphCount > ccpf2->MaxBacktrackLength )
    ccpf2->MaxBacktrackLength = cpcr->BacktrackGlyphCount;

  cpcr->Backtrack = NULL;

  count = cpcr->BacktrackGlyphCount;

  if ( ALLOC_ARRAY( cpcr->Backtrack, count, HB_UShort ) )
    return error;

  b = cpcr->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;

  cpcr->InputGlyphCount = GET_UShort();

  if ( cpcr->InputGlyphCount > ccpf2->MaxInputLength )
    ccpf2->MaxInputLength = cpcr->InputGlyphCount;

  FORGET_Frame();

  cpcr->Input = NULL;

  count = cpcr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */

  if ( ALLOC_ARRAY( cpcr->Input, count, HB_UShort ) )
    goto Fail4;

  i = cpcr->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;

  cpcr->LookaheadGlyphCount = GET_UShort();

  FORGET_Frame();

  if ( cpcr->LookaheadGlyphCount > ccpf2->MaxLookaheadLength )
    ccpf2->MaxLookaheadLength = cpcr->LookaheadGlyphCount;

  cpcr->Lookahead = NULL;

  count = cpcr->LookaheadGlyphCount;

  if ( ALLOC_ARRAY( cpcr->Lookahead, count, HB_UShort ) )
    goto Fail3;

  l = cpcr->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;

  cpcr->PosCount = GET_UShort();

  FORGET_Frame();

  cpcr->PosLookupRecord = NULL;

  count = cpcr->PosCount;

  if ( ALLOC_ARRAY( cpcr->PosLookupRecord, count, HB_PosLookupRecord ) )
    goto Fail2;

  plr = cpcr->PosLookupRecord;

  if ( ACCESS_Frame( count * 4L ) )
    goto Fail1;

  for ( n = 0; n < count; n++ )
  {
    plr[n].SequenceIndex   = GET_UShort();
    plr[n].LookupListIndex = GET_UShort();
  }

  FORGET_Frame();

  return HB_Err_Ok;

Fail1:
  FREE( plr );

Fail2:
  FREE( l );

Fail3:
  FREE( i );

Fail4:
  FREE( b );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ChainPosClassSet(
		   HB_ChainContextPosFormat2*  ccpf2,
		   HB_ChainPosClassSet*        cpcs,
		   HB_Stream                    stream )
{
  HB_Error  error;

  HB_UShort               n, m, count;
  HB_UInt                cur_offset, new_offset, base_offset;

  HB_ChainPosClassRule*  cpcr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = cpcs->ChainPosClassRuleCount = GET_UShort();

  FORGET_Frame();

  cpcs->ChainPosClassRule = NULL;

  if ( ALLOC_ARRAY( cpcs->ChainPosClassRule, count,
		    HB_ChainPosClassRule ) )
    return error;

  cpcr = cpcs->ChainPosClassRule;

  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_ChainPosClassRule( ccpf2, &cpcr[n],
					   stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_ChainPosClassRule( &cpcr[m] );

  FREE( cpcr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ChainPosRule( HB_ChainPosRule*  cpr,
				    HB_Stream          stream )
{
  HB_Error  error;

  HB_UShort             n, count;
  HB_UShort*            b;
  HB_UShort*            i;
  HB_UShort*            l;

  HB_PosLookupRecord*  plr;


  if ( ACCESS_Frame( 2L ) )
    return error;

  cpr->BacktrackGlyphCount = GET_UShort();

  FORGET_Frame();

  cpr->Backtrack = NULL;

  count = cpr->BacktrackGlyphCount;

  if ( ALLOC_ARRAY( cpr->Backtrack, count, HB_UShort ) )
    return error;

  b = cpr->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;

  cpr->InputGlyphCount = GET_UShort();

  FORGET_Frame();

  cpr->Input = NULL;

  count = cpr->InputGlyphCount - 1;  /* only InputGlyphCount - 1 elements */

  if ( ALLOC_ARRAY( cpr->Input, count, HB_UShort ) )
    goto Fail4;

  i = cpr->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;

  cpr->LookaheadGlyphCount = GET_UShort();

  FORGET_Frame();

  cpr->Lookahead = NULL;

  count = cpr->LookaheadGlyphCount;

  if ( ALLOC_ARRAY( cpr->Lookahead, count, HB_UShort ) )
    goto Fail3;

  l = cpr->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;

  cpr->PosCount = GET_UShort();

  FORGET_Frame();

  cpr->PosLookupRecord = NULL;

  count = cpr->PosCount;

  if ( ALLOC_ARRAY( cpr->PosLookupRecord, count, HB_PosLookupRecord ) )
    goto Fail2;

  plr = cpr->PosLookupRecord;

  if ( ACCESS_Frame( count * 4L ) )
    goto Fail1;

  for ( n = 0; n < count; n++ )
  {
    plr[n].SequenceIndex   = GET_UShort();
    plr[n].LookupListIndex = GET_UShort();
  }

  FORGET_Frame();

  return HB_Err_Ok;

Fail1:
  FREE( plr );

Fail2:
  FREE( l );

Fail3:
  FREE( i );

Fail4:
  FREE( b );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ChainPosRuleSet( HB_ChainPosRuleSet*  cprs,
				       HB_Stream             stream )
{
  HB_Error  error;

  HB_UShort          n, m, count;
  HB_UInt           cur_offset, new_offset, base_offset;

  HB_ChainPosRule*  cpr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = cprs->ChainPosRuleCount = GET_UShort();

  FORGET_Frame();

  cprs->ChainPosRule = NULL;

  if ( ALLOC_ARRAY( cprs->ChainPosRule, count, HB_ChainPosRule ) )
    return error;

  cpr = cprs->ChainPosRule;

  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_ChainPosRule( &cpr[n], stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_ChainPosRule( &cpr[m] );

  FREE( cpr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ContextPos1( HB_ContextPosFormat1*  cpf1,
				   HB_Stream               stream )
{
  HB_Error  error;

  HB_UShort        n, m, count;
  HB_UInt         cur_offset, new_offset, base_offset;

  HB_PosRuleSet*  prs;


  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( &cpf1->Coverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 2L ) )
    goto Fail2;

  count = cpf1->PosRuleSetCount = GET_UShort();

  FORGET_Frame();

  cpf1->PosRuleSet = NULL;

  if ( ALLOC_ARRAY( cpf1->PosRuleSet, count, HB_PosRuleSet ) )
    goto Fail2;

  prs = cpf1->PosRuleSet;

  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_PosRuleSet( &prs[n], stream ) ) != HB_Err_Ok )
      goto Fail1;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail1:
  for ( m = 0; m < n; m++ )
    Free_PosRuleSet( &prs[m] );

  FREE( prs );

Fail2:
  _HB_OPEN_Free_Coverage( &cpf1->Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ContextPos2( HB_ContextPosFormat2*  cpf2,
				   HB_Stream               stream )
{
  HB_Error  error;

  HB_UShort         n, m, count;
  HB_UInt          cur_offset, new_offset, base_offset;

  HB_PosClassSet*  pcs;


  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( &cpf2->Coverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 4L ) )
    goto Fail3;

  new_offset = GET_UShort() + base_offset;

  /* `PosClassSetCount' is the upper limit for class values, thus we
     read it now to make an additional safety check.                 */

  count = cpf2->PosClassSetCount = GET_UShort();

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_ClassDefinition( &cpf2->ClassDef, count,
				       stream ) ) != HB_Err_Ok )
    goto Fail3;
  (void)FILE_Seek( cur_offset );

  cpf2->PosClassSet      = NULL;
  cpf2->MaxContextLength = 0;

  if ( ALLOC_ARRAY( cpf2->PosClassSet, count, HB_PosClassSet ) )
    goto Fail2;

  pcs = cpf2->PosClassSet;

  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_PosClassSet( cpf2, &pcs[n],
				       stream ) ) != HB_Err_Ok )
	goto Fail1;
      (void)FILE_Seek( cur_offset );
    }
    else
    {
      /* we create a PosClassSet table with no entries */

      cpf2->PosClassSet[n].PosClassRuleCount = 0;
      cpf2->PosClassSet[n].PosClassRule      = NULL;
    }
  }

  return HB_Err_Ok;

Fail1:
  for ( m = 0; m < n; n++ )
    Free_PosClassSet( &pcs[m] );

  FREE( pcs );

Fail2:
  _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );

Fail3:
  _HB_OPEN_Free_Coverage( &cpf2->Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ContextPos3( HB_ContextPosFormat3*  cpf3,
				   HB_Stream               stream )
{
  HB_Error  error;

  HB_UShort             n, count;
  HB_UInt              cur_offset, new_offset, base_offset;

  HB_Coverage*         c;
  HB_PosLookupRecord*  plr;


  base_offset = FILE_Pos() - 2L;

  if ( ACCESS_Frame( 4L ) )
    return error;

  cpf3->GlyphCount = GET_UShort();
  cpf3->PosCount   = GET_UShort();

  FORGET_Frame();

  cpf3->Coverage = NULL;

  count = cpf3->GlyphCount;

  if ( ALLOC_ARRAY( cpf3->Coverage, count, HB_Coverage ) )
    return error;

  c = cpf3->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 );
  }

  cpf3->PosLookupRecord = NULL;

  count = cpf3->PosCount;

  if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
    goto Fail2;

  plr = cpf3->PosLookupRecord;

  if ( ACCESS_Frame( count * 4L ) )
    goto Fail1;

  for ( n = 0; n < count; n++ )
  {
    plr[n].SequenceIndex   = GET_UShort();
    plr[n].LookupListIndex = GET_UShort();
  }

  FORGET_Frame();

  return HB_Err_Ok;

Fail1:
  FREE( plr );

Fail2:
  for ( n = 0; n < count; n++ )
    _HB_OPEN_Free_Coverage( &c[n] );

  FREE( c );
  return error;
}

Project: harfbuzz

static HB_Error  Load_CursivePos( HB_GPOS_SubTable* st,
				  HB_Stream        stream )
{
  HB_Error  error;
  HB_CursivePos*  cp = &st->cursive;

  HB_UShort             n, m, count;
  HB_UInt              cur_offset, new_offset, base_offset;

  HB_EntryExitRecord*  eer;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 4L ) )
    return error;

  cp->PosFormat = GET_UShort();
  new_offset    = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &cp->Coverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 2L ) )
    goto Fail2;

  count = cp->EntryExitCount = GET_UShort();

  FORGET_Frame();

  cp->EntryExitRecord = NULL;

  if ( ALLOC_ARRAY( cp->EntryExitRecord, count, HB_EntryExitRecord ) )
    goto Fail2;

  eer = cp->EntryExitRecord;

  for ( n = 0; n < count; n++ )
  {
    HB_UInt entry_offset;

    if ( ACCESS_Frame( 2L ) )
      return error;

    entry_offset = new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = Load_Anchor( &eer[n].EntryAnchor,
				  stream ) ) != HB_Err_Ok )
	goto Fail1;
      (void)FILE_Seek( cur_offset );
    }
    else
      eer[n].EntryAnchor.PosFormat   = 0;

    if ( ACCESS_Frame( 2L ) )
      return error;

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = Load_Anchor( &eer[n].ExitAnchor,
				  stream ) ) != HB_Err_Ok )
      {
	if ( entry_offset )
	  Free_Anchor( &eer[n].EntryAnchor );
	goto Fail1;
      }
      (void)FILE_Seek( cur_offset );
    }
    else
      eer[n].ExitAnchor.PosFormat   = 0;
  }

  return HB_Err_Ok;

Fail1:
  for ( m = 0; m < n; m++ )
  {
    Free_Anchor( &eer[m].EntryAnchor );
    Free_Anchor( &eer[m].ExitAnchor );
  }

  FREE( eer );

Fail2:
  _HB_OPEN_Free_Coverage( &cp->Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_LigatureArray( HB_LigatureArray*  la,
				     HB_UShort           num_classes,
				     HB_Stream           stream )
{
  HB_Error  error;

  HB_UShort            n, m, count;
  HB_UInt             cur_offset, new_offset, base_offset;

  HB_LigatureAttach*  lat;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = la->LigatureCount = GET_UShort();

  FORGET_Frame();

  la->LigatureAttach = NULL;

  if ( ALLOC_ARRAY( la->LigatureAttach, count, HB_LigatureAttach ) )
    return error;

  lat = la->LigatureAttach;

  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_LigatureAttach( &lat[n], num_classes,
					stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_LigatureAttach( &lat[m], num_classes );

  FREE( lat );
  return error;
}

Project: harfbuzz

static HB_Error  Load_LigatureAttach( HB_LigatureAttach*  lat,
				      HB_UShort            num_classes,
				      HB_Stream            stream )
{
  HB_Error  error;

  HB_UShort             m, n, k, count;
  HB_UInt              cur_offset, new_offset, base_offset;

  HB_ComponentRecord*  cr;
  HB_Anchor*           lan;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = lat->ComponentCount = GET_UShort();

  FORGET_Frame();

  lat->ComponentRecord = NULL;

  if ( ALLOC_ARRAY( lat->ComponentRecord, count, HB_ComponentRecord ) )
    return error;

  cr = lat->ComponentRecord;

  for ( m = 0; m < count; m++ )
  {
    cr[m].LigatureAnchor = NULL;

    if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, HB_Anchor ) )
      goto Fail;

    lan = cr[m].LigatureAnchor;

    for ( n = 0; n < num_classes; n++ )
    {
      if ( ACCESS_Frame( 2L ) )
	goto Fail0;

      new_offset = GET_UShort();

      FORGET_Frame();

      if ( new_offset )
      {
	new_offset += base_offset;

	cur_offset = FILE_Pos();
	if ( FILE_Seek( new_offset ) ||
	     ( error = Load_Anchor( &lan[n], stream ) ) != HB_Err_Ok )
	  goto Fail0;
	(void)FILE_Seek( cur_offset );
      }
      else
	lan[n].PosFormat = 0;
    }

    continue;
  Fail0:
    for ( k = 0; k < n; k++ )
      Free_Anchor( &lan[k] );
    goto Fail;
  }

  return HB_Err_Ok;

Fail:
  for ( k = 0; k < m; k++ )
  {
    lan = cr[k].LigatureAnchor;

    for ( n = 0; n < num_classes; n++ )
      Free_Anchor( &lan[n] );

    FREE( lan );
  }

  FREE( cr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_Mark2Array( HB_Mark2Array*  m2a,
				  HB_UShort        num_classes,
				  HB_Stream        stream )
{
  HB_Error  error;

  HB_UShort        m, n, count;
  HB_UInt          cur_offset, new_offset, base_offset;

  HB_Mark2Record  *m2r;
  HB_Anchor       *m2an, *m2ans;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = m2a->Mark2Count = GET_UShort();

  FORGET_Frame();

  m2a->Mark2Record = NULL;

  if ( ALLOC_ARRAY( m2a->Mark2Record, count, HB_Mark2Record ) )
    return error;

  m2r = m2a->Mark2Record;

  m2ans = NULL;

  if ( ALLOC_ARRAY( m2ans, count * num_classes, HB_Anchor ) )
    goto Fail;

  for ( m = 0; m < count; m++ )
  {
    m2an = m2r[m].Mark2Anchor = m2ans + m * num_classes;

    for ( n = 0; n < num_classes; n++ )
    {
      if ( ACCESS_Frame( 2L ) )
	goto Fail;

      new_offset = GET_UShort() + base_offset;

      FORGET_Frame();

      if (new_offset == base_offset) {
        /* Anchor table not provided.  Skip loading.
	 * Some versions of FreeSans hit this. */
        m2an[n].PosFormat = 0;
	continue;
      }

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = Load_Anchor( &m2an[n], stream ) ) != HB_Err_Ok )
	goto Fail;
      (void)FILE_Seek( cur_offset );
    }
  }

  return HB_Err_Ok;

Fail:
  FREE( m2ans );
  FREE( m2r );
  return error;
}

Project: harfbuzz

static HB_Error  Load_MarkArray ( HB_MarkArray*  ma,
				  HB_Stream       stream )
{
  HB_Error  error;

  HB_UShort        n, m, count;
  HB_UInt         cur_offset, new_offset, base_offset;

  HB_MarkRecord*  mr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = ma->MarkCount = GET_UShort();

  FORGET_Frame();

  ma->MarkRecord = NULL;

  if ( ALLOC_ARRAY( ma->MarkRecord, count, HB_MarkRecord ) )
    return error;

  mr = ma->MarkRecord;

  for ( n = 0; n < count; n++ )
  {
    if ( ACCESS_Frame( 4L ) )
      goto Fail;

    mr[n].Class = GET_UShort();
    new_offset  = GET_UShort() + base_offset;

    FORGET_Frame();

    cur_offset = FILE_Pos();
    if ( FILE_Seek( new_offset ) ||
	 ( error = Load_Anchor( &mr[n].MarkAnchor, stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_Anchor( &mr[m].MarkAnchor );

  FREE( mr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_MarkBasePos( HB_GPOS_SubTable* st,
				   HB_Stream         stream )
{
  HB_Error  error;
  HB_MarkBasePos* mbp = &st->markbase;

  HB_UInt  cur_offset, new_offset, base_offset;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 4L ) )
    return error;

  mbp->PosFormat = GET_UShort();
  new_offset     = GET_UShort() + base_offset;

  FORGET_Frame();

  if (mbp->PosFormat != 1)
    return ERR(HB_Err_Invalid_SubTable_Format);

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &mbp->MarkCoverage, stream ) ) != HB_Err_Ok )
    return error;
  (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_Coverage( &mbp->BaseCoverage, stream ) ) != HB_Err_Ok )
    goto Fail3;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 4L ) )
    goto Fail2;

  mbp->ClassCount = GET_UShort();
  new_offset      = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = Load_MarkArray( &mbp->MarkArray, stream ) ) != HB_Err_Ok )
    goto Fail2;
  (void)FILE_Seek( cur_offset );

  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_BaseArray( &mbp->BaseArray, mbp->ClassCount,
				 stream ) ) != HB_Err_Ok )
    goto Fail1;

  return HB_Err_Ok;

Fail1:
  Free_MarkArray( &mbp->MarkArray );

Fail2:
  _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );

Fail3:
  _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_MarkMarkPos( HB_GPOS_SubTable* st,
				   HB_Stream         stream )
{
  HB_Error  error;
  HB_MarkMarkPos* mmp = &st->markmark;

  HB_UInt  cur_offset, new_offset, base_offset;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 4L ) )
    return error;

  mmp->PosFormat = GET_UShort();
  new_offset     = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &mmp->Mark1Coverage,
				stream ) ) != HB_Err_Ok )
    return error;
  (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_Coverage( &mmp->Mark2Coverage,
				stream ) ) != HB_Err_Ok )
    goto Fail3;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 4L ) )
    goto Fail2;

  mmp->ClassCount = GET_UShort();
  new_offset      = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = Load_MarkArray( &mmp->Mark1Array, stream ) ) != HB_Err_Ok )
    goto Fail2;
  (void)FILE_Seek( cur_offset );

  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_Mark2Array( &mmp->Mark2Array, mmp->ClassCount,
				  stream ) ) != HB_Err_Ok )
    goto Fail1;

  return HB_Err_Ok;

Fail1:
  Free_MarkArray( &mmp->Mark1Array );

Fail2:
  _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );

Fail3:
  _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PairPos( HB_GPOS_SubTable* st,
			       HB_Stream     stream )
{
  HB_Error  error;
  HB_PairPos*     pp = &st->pair;

  HB_UShort         format1, format2;
  HB_UInt          cur_offset, new_offset, base_offset;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 8L ) )
    return error;

  pp->PosFormat = GET_UShort();
  new_offset    = GET_UShort() + base_offset;

  format1 = pp->ValueFormat1 = GET_UShort();
  format2 = pp->ValueFormat2 = GET_UShort();

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &pp->Coverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  switch ( pp->PosFormat )
  {
  case 1:
    error = Load_PairPos1( &pp->ppf.ppf1, format1, format2, stream );
    if ( error )
      goto Fail;
    break;

  case 2:
    error = Load_PairPos2( &pp->ppf.ppf2, format1, format2, stream );
    if ( error )
      goto Fail;
    break;

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  return HB_Err_Ok;

Fail:
  _HB_OPEN_Free_Coverage( &pp->Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PairPos1( HB_PairPosFormat1*  ppf1,
				HB_UShort            format1,
				HB_UShort            format2,
				HB_Stream            stream )
{
  HB_Error  error;

  HB_UShort     n, m, count;
  HB_UInt      cur_offset, new_offset, base_offset;

  HB_PairSet*  ps;


  base_offset = FILE_Pos() - 8L;

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = ppf1->PairSetCount = GET_UShort();

  FORGET_Frame();

  ppf1->PairSet = NULL;

  if ( ALLOC_ARRAY( ppf1->PairSet, count, HB_PairSet ) )
    return error;

  ps = ppf1->PairSet;

  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_PairSet( &ps[n], format1,
				 format2, stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_PairSet( &ps[m], format1, format2 );

  FREE( ps );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PairPos2( HB_PairPosFormat2*  ppf2,
				HB_UShort            format1,
				HB_UShort            format2,
				HB_Stream            stream )
{
  HB_Error  error;

  HB_UShort          m, n, k, count1, count2;
  HB_UInt           cur_offset, new_offset1, new_offset2, base_offset;

  HB_Class1Record*  c1r;
  HB_Class2Record*  c2r;


  base_offset = FILE_Pos() - 8L;

  if ( ACCESS_Frame( 8L ) )
    return error;

  new_offset1 = GET_UShort() + base_offset;
  new_offset2 = GET_UShort() + base_offset;

  /* `Class1Count' and `Class2Count' are the upper limits for class
     values, thus we read it now to make additional safety checks.  */

  count1 = ppf2->Class1Count = GET_UShort();
  count2 = ppf2->Class2Count = GET_UShort();

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset1 ) ||
       ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef1, count1,
				       stream ) ) != HB_Err_Ok )
    return error;
  if ( FILE_Seek( new_offset2 ) ||
       ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef2, count2,
				       stream ) ) != HB_Err_Ok )
    goto Fail3;
  (void)FILE_Seek( cur_offset );

  ppf2->Class1Record = NULL;

  if ( ALLOC_ARRAY( ppf2->Class1Record, count1, HB_Class1Record ) )
    goto Fail2;

  c1r = ppf2->Class1Record;

  for ( m = 0; m < count1; m++ )
  {
    c1r[m].Class2Record = NULL;

    if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, HB_Class2Record ) )
      goto Fail1;

    c2r = c1r[m].Class2Record;

    for ( n = 0; n < count2; n++ )
    {
      if ( format1 )
      {
	error = Load_ValueRecord( &c2r[n].Value1, format1,
				  base_offset, stream );
	if ( error )
	  goto Fail0;
      }
      if ( format2 )
      {
	error = Load_ValueRecord( &c2r[n].Value2, format2,
				  base_offset, stream );
	if ( error )
	{
	  if ( format1 )
	    Free_ValueRecord( &c2r[n].Value1, format1 );
	  goto Fail0;
	}
      }
    }

    continue;

  Fail0:
    for ( k = 0; k < n; k++ )
    {
      if ( format1 )
	Free_ValueRecord( &c2r[k].Value1, format1 );
      if ( format2 )
	Free_ValueRecord( &c2r[k].Value2, format2 );
    }
    goto Fail1;
  }

  return HB_Err_Ok;

Fail1:
  for ( k = 0; k < m; k++ )
  {
    c2r = c1r[k].Class2Record;

    for ( n = 0; n < count2; n++ )
    {
      if ( format1 )
	Free_ValueRecord( &c2r[n].Value1, format1 );
      if ( format2 )
	Free_ValueRecord( &c2r[n].Value2, format2 );
    }

    FREE( c2r );
  }

  FREE( c1r );
Fail2:

  _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );

Fail3:
  _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PairSet ( HB_PairSet*  ps,
				HB_UShort     format1,
				HB_UShort     format2,
				HB_Stream     stream )
{
  HB_Error  error;

  HB_UShort             n, m, count;
  HB_UInt              base_offset;

  HB_PairValueRecord*  pvr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = ps->PairValueCount = GET_UShort();

  FORGET_Frame();

  ps->PairValueRecord = NULL;

  if ( ALLOC_ARRAY( ps->PairValueRecord, count, HB_PairValueRecord ) )
    return error;

  pvr = ps->PairValueRecord;

  for ( n = 0; n < count; n++ )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail;

    pvr[n].SecondGlyph = GET_UShort();

    FORGET_Frame();

    if ( format1 )
    {
      error = Load_ValueRecord( &pvr[n].Value1, format1,
				base_offset, stream );
      if ( error )
	goto Fail;
    }
    if ( format2 )
    {
      error = Load_ValueRecord( &pvr[n].Value2, format2,
				base_offset, stream );
      if ( error )
      {
	if ( format1 )
	  Free_ValueRecord( &pvr[n].Value1, format1 );
	goto Fail;
      }
    }
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
  {
    if ( format1 )
      Free_ValueRecord( &pvr[m].Value1, format1 );
    if ( format2 )
      Free_ValueRecord( &pvr[m].Value2, format2 );
  }

  FREE( pvr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PosClassRule( HB_ContextPosFormat2*  cpf2,
				    HB_PosClassRule*       pcr,
				    HB_Stream               stream )
{
  HB_Error  error;

  HB_UShort             n, count;

  HB_UShort*            c;
  HB_PosLookupRecord*  plr;


  if ( ACCESS_Frame( 4L ) )
    return error;

  pcr->GlyphCount = GET_UShort();
  pcr->PosCount   = GET_UShort();

  FORGET_Frame();

  if ( pcr->GlyphCount > cpf2->MaxContextLength )
    cpf2->MaxContextLength = pcr->GlyphCount;

  pcr->Class = NULL;

  count = pcr->GlyphCount - 1;        /* only GlyphCount - 1 elements */

  if ( ALLOC_ARRAY( pcr->Class, count, HB_UShort ) )
    return error;

  c = pcr->Class;

  if ( ACCESS_Frame( count * 2L ) )
    goto Fail2;

  for ( n = 0; n < count; n++ )
    c[n] = GET_UShort();

  FORGET_Frame();

  pcr->PosLookupRecord = NULL;

  count = pcr->PosCount;

  if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, HB_PosLookupRecord ) )
    goto Fail2;

  plr = pcr->PosLookupRecord;

  if ( ACCESS_Frame( count * 4L ) )
    goto Fail1;

  for ( n = 0; n < count; n++ )
  {
    plr[n].SequenceIndex   = GET_UShort();
    plr[n].LookupListIndex = GET_UShort();
  }

  FORGET_Frame();

  return HB_Err_Ok;

Fail1:
  FREE( plr );

Fail2:
  FREE( c );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PosClassSet( HB_ContextPosFormat2*  cpf2,
				   HB_PosClassSet*        pcs,
				   HB_Stream               stream )
{
  HB_Error  error;

  HB_UShort          n, m, count;
  HB_UInt           cur_offset, new_offset, base_offset;

  HB_PosClassRule*  pcr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = pcs->PosClassRuleCount = GET_UShort();

  FORGET_Frame();

  pcs->PosClassRule = NULL;

  if ( ALLOC_ARRAY( pcs->PosClassRule, count, HB_PosClassRule ) )
    return error;

  pcr = pcs->PosClassRule;

  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_PosClassRule( cpf2, &pcr[n],
				      stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_PosClassRule( &pcr[m] );

  FREE( pcr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PosRuleSet( HB_PosRuleSet*  prs,
				  HB_Stream        stream )
{
  HB_Error  error;

  HB_UShort     n, m, count;
  HB_UInt      cur_offset, new_offset, base_offset;

  HB_PosRule*  pr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 2L ) )
    return error;

  count = prs->PosRuleCount = GET_UShort();

  FORGET_Frame();

  prs->PosRule = NULL;

  if ( ALLOC_ARRAY( prs->PosRule, count, HB_PosRule ) )
    return error;

  pr = prs->PosRule;

  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_PosRule( &pr[n], stream ) ) != HB_Err_Ok )
      goto Fail;
    (void)FILE_Seek( cur_offset );
  }

  return HB_Err_Ok;

Fail:
  for ( m = 0; m < n; m++ )
    Free_PosRule( &pr[m] );

  FREE( pr );
  return error;
}

Project: harfbuzz

static HB_Error  Load_SinglePos( HB_GPOS_SubTable* st,
				 HB_Stream       stream )
{
  HB_Error  error;
  HB_SinglePos*   sp = &st->single;

  HB_UShort         n, m, count, format;
  HB_UInt          cur_offset, new_offset, base_offset;

  HB_ValueRecord*  vr;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 6L ) )
    return error;

  sp->PosFormat = GET_UShort();
  new_offset    = GET_UShort() + base_offset;

  format = sp->ValueFormat = GET_UShort();

  FORGET_Frame();

  if ( !format )
    return ERR(HB_Err_Invalid_SubTable);

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &sp->Coverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  switch ( sp->PosFormat )
  {
  case 1:
    error = Load_ValueRecord( &sp->spf.spf1.Value, format,
			      base_offset, stream );
    if ( error )
      goto Fail2;
    break;

  case 2:
    if ( ACCESS_Frame( 2L ) )
      goto Fail2;

    count = sp->spf.spf2.ValueCount = GET_UShort();

    FORGET_Frame();

    sp->spf.spf2.Value = NULL;

    if ( ALLOC_ARRAY( sp->spf.spf2.Value, count, HB_ValueRecord ) )
      goto Fail2;

    vr = sp->spf.spf2.Value;

    for ( n = 0; n < count; n++ )
    {
      error = Load_ValueRecord( &vr[n], format, base_offset, stream );
      if ( error )
	goto Fail1;
    }
    break;

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  return HB_Err_Ok;

Fail1:
  for ( m = 0; m < n; m++ )
    Free_ValueRecord( &vr[m], format );

  FREE( vr );

Fail2:
  _HB_OPEN_Free_Coverage( &sp->Coverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_ValueRecord( HB_ValueRecord*  vr,
				   HB_UShort         format,
				   HB_UInt          base_offset,
				   HB_Stream         stream )
{
  HB_Error  error;

  HB_UInt cur_offset, new_offset;


  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
  {
    if ( ACCESS_Frame( 2L ) )
      return error;

    vr->XPlacement = GET_Short();

    FORGET_Frame();
  }
  else
    vr->XPlacement = 0;

  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
  {
    if ( ACCESS_Frame( 2L ) )
      return error;

    vr->YPlacement = GET_Short();

    FORGET_Frame();
  }
  else
    vr->YPlacement = 0;

  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
  {
    if ( ACCESS_Frame( 2L ) )
      return error;

    vr->XAdvance = GET_Short();

    FORGET_Frame();
  }
  else
    vr->XAdvance = 0;

  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
  {
    if ( ACCESS_Frame( 2L ) )
      return error;

    vr->YAdvance = GET_Short();

    FORGET_Frame();
  }
  else
    vr->YAdvance = 0;

  if ( format & HB_GPOS_FORMAT_HAVE_DEVICE_TABLES )
  {
    if ( ALLOC_ARRAY( vr->DeviceTables, 4, HB_Device ) )
      return error;
    vr->DeviceTables[VR_X_ADVANCE_DEVICE] = 0;
    vr->DeviceTables[VR_Y_ADVANCE_DEVICE] = 0;
    vr->DeviceTables[VR_X_PLACEMENT_DEVICE] = 0;
    vr->DeviceTables[VR_Y_PLACEMENT_DEVICE] = 0;
  }
  else
  {
    vr->DeviceTables = 0;
  }

  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail4;

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = _HB_OPEN_Load_Device( &vr->DeviceTables[VR_X_PLACEMENT_DEVICE],
				  stream ) ) != HB_Err_Ok )
       goto Fail4;
      (void)FILE_Seek( cur_offset );
    }
  }

  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail3;

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = _HB_OPEN_Load_Device( &vr->DeviceTables[VR_Y_PLACEMENT_DEVICE],
				  stream ) ) != HB_Err_Ok )
	goto Fail3;
      (void)FILE_Seek( cur_offset );
    }
  }

  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail2;

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = _HB_OPEN_Load_Device( &vr->DeviceTables[VR_X_ADVANCE_DEVICE],
				  stream ) ) != HB_Err_Ok )
	goto Fail2;
      (void)FILE_Seek( cur_offset );
    }
  }

  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail1;

    new_offset = GET_UShort();

    FORGET_Frame();

    if ( new_offset )
    {
      new_offset += base_offset;

      cur_offset = FILE_Pos();
      if ( FILE_Seek( new_offset ) ||
	   ( error = _HB_OPEN_Load_Device( &vr->DeviceTables[VR_Y_ADVANCE_DEVICE],
				  stream ) ) != HB_Err_Ok )
	goto Fail1;
      (void)FILE_Seek( cur_offset );
    }
  }

  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail1;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
    vr->XIdPlacement = GET_UShort();
#else
    (void) GET_UShort();
#endif

    FORGET_Frame();
  }
#ifdef HB_SUPPORT_MULTIPLE_MASTER
  else
    vr->XIdPlacement = 0;
#endif

  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail1;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
    vr->YIdPlacement = GET_UShort();
#else
    (void) GET_UShort();
#endif

    FORGET_Frame();
  }
#ifdef HB_SUPPORT_MULTIPLE_MASTER
  else
    vr->YIdPlacement = 0;
#endif

  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail1;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
    vr->XIdAdvance = GET_UShort();
#else
    (void) GET_UShort();
#endif

    FORGET_Frame();
  }
#ifdef HB_SUPPORT_MULTIPLE_MASTER
  else
    vr->XIdAdvance = 0;
#endif

  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
  {
    if ( ACCESS_Frame( 2L ) )
      goto Fail1;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
    vr->YIdAdvance = GET_UShort();
#else
    (void) GET_UShort();
#endif

    FORGET_Frame();
  }
#ifdef HB_SUPPORT_MULTIPLE_MASTER
  else
    vr->YIdAdvance = 0;
#endif

  return HB_Err_Ok;

Fail1:
  if ( vr->DeviceTables )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_ADVANCE_DEVICE] );

Fail2:
  if ( vr->DeviceTables )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_X_ADVANCE_DEVICE] );

Fail3:
  if ( vr->DeviceTables )
    _HB_OPEN_Free_Device( vr->DeviceTables[VR_Y_PLACEMENT_DEVICE] );

Fail4:
  FREE( vr->DeviceTables );
  return error;
}

Project: harfbuzz

static HB_Error  Lookup_ChainContextPos(
		   GPOS_Instance*        gpi,
		   HB_GPOS_SubTable* st,
		   HB_Buffer            buffer,
		   HB_UShort             flags,
		   HB_UShort             context_length,
		   int                   nesting_level )
{
  HB_ChainContextPos*  ccp = &st->chain;

  switch ( ccp->PosFormat )
  {
  case 1:
    return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, buffer,
				    flags, context_length,
				    nesting_level );

  case 2:
    return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, buffer,
				    flags, context_length,
				    nesting_level );

  case 3:
    return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, buffer,
				    flags, context_length,
				    nesting_level );

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  return HB_Err_Ok;               /* never reached */
}

Project: harfbuzz

static HB_Error  Lookup_ChainContextPos1(
		   GPOS_Instance*               gpi,
		   HB_ChainContextPosFormat1*  ccpf1,
		   HB_Buffer                   buffer,
		   HB_UShort                    flags,
		   HB_UShort                    context_length,
		   int                          nesting_level )
{
  HB_UShort          index, property;
  HB_UShort          i, j, k, num_cpr;
  HB_UShort          bgc, igc, lgc;
  HB_Error           error;
  HB_GPOSHeader*    gpos = gpi->gpos;

  HB_ChainPosRule*  cpr;
  HB_ChainPosRule   curr_cpr;
  HB_GDEFHeader*    gdef;


  gdef = gpos->gdef;

  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
    return error;

  error = _HB_OPEN_Coverage_Index( &ccpf1->Coverage, IN_CURGLYPH(), &index );
  if ( error )
    return error;

  cpr     = ccpf1->ChainPosRuleSet[index].ChainPosRule;
  num_cpr = ccpf1->ChainPosRuleSet[index].ChainPosRuleCount;

  for ( k = 0; k < num_cpr; k++ )
  {
    curr_cpr = cpr[k];
    bgc      = curr_cpr.BacktrackGlyphCount;
    igc      = curr_cpr.InputGlyphCount;
    lgc      = curr_cpr.LookaheadGlyphCount;

    if ( context_length != 0xFFFF && context_length < igc )
      goto next_chainposrule;

    /* check whether context is too long; it is a first guess only */

    if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
      goto next_chainposrule;

    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->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 )
	    goto next_chainposrule;
	  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 ( IN_GLYPH( j ) != curr_cpr.Backtrack[i] )
	  goto next_chainposrule;
      }
    }

    /* 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_chainposrule;
	j++;
      }

      if ( IN_GLYPH( j ) != curr_cpr.Input[i - 1] )
	goto next_chainposrule;
    }

    /* 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_chainposrule;
	j++;
      }

      if ( IN_GLYPH( j ) != curr_cpr.Lookahead[i] )
	goto next_chainposrule;
    }

    return Do_ContextPos( gpi, igc,
			  curr_cpr.PosCount,
			  curr_cpr.PosLookupRecord,
			  buffer,
			  nesting_level );

  next_chainposrule:
    ;
  }

  return HB_Err_Not_Covered;
}

Project: harfbuzz

static HB_Error  Lookup_ChainContextPos2(
		   GPOS_Instance*               gpi,
		   HB_ChainContextPosFormat2*  ccpf2,
		   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_GPOSHeader*        gpos = gpi->gpos;

  HB_ChainPosClassSet*  cpcs;
  HB_ChainPosClassRule  cpcr;
  HB_GDEFHeader*        gdef;


  gdef = gpos->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( &ccpf2->Coverage, IN_CURGLYPH(), &index );
  if ( error )
    return error;

  if ( ALLOC_ARRAY( backtrack_classes, ccpf2->MaxBacktrackLength, HB_UShort ) )
    return error;
  known_backtrack_classes = 0;

  if (ccpf2->MaxInputLength < 1)
    return HB_Err_Not_Covered;

  if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, HB_UShort ) )
    goto End3;
  known_input_classes = 1;

  if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, HB_UShort ) )
    goto End2;
  known_lookahead_classes = 0;

  error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_CURGLYPH(),
		     &input_classes[0], NULL );
  if ( error && error != HB_Err_Not_Covered )
    goto End1;

  cpcs = &ccpf2->ChainPosClassSet[input_classes[0]];
  if ( !cpcs )
  {
    error = ERR(HB_Err_Invalid_SubTable);
    goto End1;
  }

  for ( k = 0; k < cpcs->ChainPosClassRuleCount; k++ )
  {
    cpcr = cpcs->ChainPosClassRule[k];
    bgc  = cpcr.BacktrackGlyphCount;
    igc  = cpcr.InputGlyphCount;
    lgc  = cpcr.LookaheadGlyphCount;

    if ( context_length != 0xFFFF && context_length < igc )
      goto next_chainposclassrule;

    /* check whether context is too long; it is a first guess only */

    if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
      goto next_chainposclassrule;

    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       = cpcr.Backtrack;

      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 )
	    goto End1;

	  if ( j + 1 == bgc - i )
	    goto next_chainposclassrule;
	  j++;
	}

	if ( i >= known_backtrack_classes )
	{
	  /* Keeps us from having to do this for each rule */

	  error = _HB_OPEN_Get_Class( &ccpf2->BacktrackClassDef, IN_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_chainposclassrule;
      }
    }

    ic       = cpcr.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_chainposclassrule;
	j++;
      }

      if ( i >= known_input_classes )
      {
	error = _HB_OPEN_Get_Class( &ccpf2->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_chainposclassrule;
    }

    /* we are starting to check for lookahead glyphs right after the
       last context glyph                                            */

    lc       = cpcr.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_chainposclassrule;
	j++;
      }

      if ( i >= known_lookahead_classes )
      {
	error = _HB_OPEN_Get_Class( &ccpf2->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_chainposclassrule;
    }

    error = Do_ContextPos( gpi, igc,
			   cpcr.PosCount,
			   cpcr.PosLookupRecord,
			   buffer,
			   nesting_level );
    goto End1;

  next_chainposclassrule:
    ;
  }

  error = HB_Err_Not_Covered;

End1:
  FREE( lookahead_classes );

End2:
  FREE( input_classes );

End3:
  FREE( backtrack_classes );
  return error;
}

Project: harfbuzz

static HB_Error  Lookup_ChainContextPos3(
		   GPOS_Instance*               gpi,
		   HB_ChainContextPosFormat3*  ccpf3,
		   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_GPOSHeader*  gpos = gpi->gpos;

  HB_Coverage*    bc;
  HB_Coverage*    ic;
  HB_Coverage*    lc;
  HB_GDEFHeader*  gdef;


  gdef = gpos->gdef;

  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
    return error;

  bgc = ccpf3->BacktrackGlyphCount;
  igc = ccpf3->InputGlyphCount;
  lgc = ccpf3->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->in_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       = ccpf3->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;
    }
  }

  ic       = ccpf3->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 to check for lookahead glyphs right after the
     last context glyph                                            */

  lc       = ccpf3->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_ContextPos( gpi, igc,
			ccpf3->PosCount,
			ccpf3->PosLookupRecord,
			buffer,
			nesting_level );
}

Project: harfbuzz

static HB_Error  Lookup_ContextPos( GPOS_Instance*    gpi,
				    HB_GPOS_SubTable* st,
				    HB_Buffer        buffer,
				    HB_UShort         flags,
				    HB_UShort         context_length,
				    int               nesting_level )
{
  HB_ContextPos*   cp = &st->context;

  switch ( cp->PosFormat )
  {
  case 1:
    return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, buffer,
			       flags, context_length, nesting_level );

  case 2:
    return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, buffer,
			       flags, context_length, nesting_level );

  case 3:
    return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, buffer,
			       flags, context_length, nesting_level );

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  return HB_Err_Ok;               /* never reached */
}

Project: harfbuzz

static HB_Error  Lookup_ContextPos2( GPOS_Instance*          gpi,
				     HB_ContextPosFormat2*  cpf2,
				     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_GPOSHeader*    gpos = gpi->gpos;

  HB_PosClassSet*   pcs;
  HB_PosClassRule*  pr;
  HB_GDEFHeader*    gdef;


  gdef = gpos->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( &cpf2->Coverage, IN_CURGLYPH(), &index );
  if ( error )
    return error;

  if (cpf2->MaxContextLength < 1)
    return HB_Err_Not_Covered;

  if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, HB_UShort ) )
    return error;

  error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_CURGLYPH(),
		     &classes[0], NULL );
  if ( error && error != HB_Err_Not_Covered )
    goto End;
  known_classes = 0;

  pcs = &cpf2->PosClassSet[classes[0]];
  if ( !pcs )
  {
    error = ERR(HB_Err_Invalid_SubTable);
    goto End;
  }

  for ( k = 0; k < pcs->PosClassRuleCount; k++ )
  {
    pr = &pcs->PosClassRule[k];

    if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
      goto next_posclassrule;

    if ( buffer->in_pos + pr->GlyphCount > buffer->in_length )
      goto next_posclassrule;                /* context is too long */

    cl   = pr->Class;

    /* Start at 1 because [0] is implied */

    for ( i = 1, j = buffer->in_pos + 1; i < pr->GlyphCount; i++, j++ )
    {
      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
      {
	if ( error && error != HB_Err_Not_Covered )
	  goto End;

	if ( j + pr->GlyphCount - i == (HB_Int)buffer->in_length )
	  goto next_posclassrule;
	j++;
      }

      if ( i > known_classes )
      {
	/* Keeps us from having to do this for each rule */

	error = _HB_OPEN_Get_Class( &cpf2->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_posclassrule;
    }

    error = Do_ContextPos( gpi, pr->GlyphCount,
			   pr->PosCount, pr->PosLookupRecord,
			   buffer,
			   nesting_level );
    goto End;

  next_posclassrule:
    ;
  }

  error = HB_Err_Not_Covered;

End:
  FREE( classes );
  return error;
}

Project: harfbuzz

static HB_Error  Lookup_ContextPos3( GPOS_Instance*          gpi,
				     HB_ContextPosFormat3*  cpf3,
				     HB_Buffer              buffer,
				     HB_UShort               flags,
				     HB_UShort               context_length,
				     int                     nesting_level )
{
  HB_Error         error;
  HB_UShort        index, i, j, property;
  HB_GPOSHeader*  gpos = gpi->gpos;

  HB_Coverage*    c;
  HB_GDEFHeader*  gdef;


  gdef = gpos->gdef;

  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
    return error;

  if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
    return HB_Err_Not_Covered;

  if ( buffer->in_pos + cpf3->GlyphCount > buffer->in_length )
    return HB_Err_Not_Covered;         /* context is too long */

  c    = cpf3->Coverage;

  for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
  {
    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
    {
      if ( error && error != HB_Err_Not_Covered )
	return error;

      if ( j + cpf3->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_ContextPos( gpi, cpf3->GlyphCount,
			cpf3->PosCount, cpf3->PosLookupRecord,
			buffer,
			nesting_level );
}

Project: harfbuzz

static HB_Error  Lookup_CursivePos( GPOS_Instance*    gpi,
				    HB_GPOS_SubTable* st,
				    HB_Buffer        buffer,
				    HB_UShort         flags,
				    HB_UShort         context_length,
				    int               nesting_level )
{
  HB_UShort        index, property;
  HB_Error         error;
  HB_GPOSHeader*  gpos = gpi->gpos;
  HB_CursivePos*  cp = &st->cursive;

  HB_EntryExitRecord*  eer;
  HB_Fixed                entry_x, entry_y;
  HB_Fixed                exit_x, exit_y;

  HB_UNUSED(nesting_level);

  if ( context_length != 0xFFFF && context_length < 1 )
  {
    gpi->last = 0xFFFF;
    return HB_Err_Not_Covered;
  }

  /* Glyphs not having the right GDEF properties will be ignored, i.e.,
     gpi->last won't be reset (contrary to user defined properties). */

  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
    return error;

  /* We don't handle mark glyphs here.  According to Andrei, this isn't
     possible, but who knows...                                         */

  if ( property == HB_GDEF_MARK )
  {
    gpi->last = 0xFFFF;
    return HB_Err_Not_Covered;
  }

  error = _HB_OPEN_Coverage_Index( &cp->Coverage, IN_CURGLYPH(), &index );
  if ( error )
  {
    gpi->last = 0xFFFF;
    return error;
  }

  if ( index >= cp->EntryExitCount )
    return ERR(HB_Err_Invalid_SubTable);

  eer = &cp->EntryExitRecord[index];

  /* Now comes the messiest part of the whole OpenType
     specification.  At first glance, cursive connections seem easy
     to understand, but there are pitfalls!  The reason is that
     the specs don't mention how to compute the advance values
     resp. glyph offsets.  I was told it would be an omission, to
     be fixed in the next OpenType version...  Again many thanks to
     Andrei Burago <andreib@microsoft.com> for clarifications.

     Consider the following example:

		      |  xadv1    |
		       +---------+
		       |         |
		 +-----+--+ 1    |
		 |     | .|      |
		 |    0+--+------+
		 |   2    |
		 |        |
		0+--------+
		|  xadv2   |

       glyph1: advance width = 12
	       anchor point = (3,1)

       glyph2: advance width = 11
	       anchor point = (9,4)

       LSB is 1 for both glyphs (so the boxes drawn above are glyph
       bboxes).  Writing direction is R2L; `0' denotes the glyph's
       coordinate origin.

     Now the surprising part: The advance width of the *left* glyph
     (resp. of the *bottom* glyph) will be modified, no matter
     whether the writing direction is L2R or R2L (resp. T2B or
     B2T)!  This assymetry is caused by the fact that the glyph's
     coordinate origin is always the lower left corner for all
     writing directions.

     Continuing the above example, we can compute the new
     (horizontal) advance width of glyph2 as

       9 - 3 = 6  ,

     and the new vertical offset of glyph2 as

       1 - 4 = -3  .


     Vertical writing direction is far more complicated:

     a) Assuming that we recompute the advance height of the lower glyph:

				  --
		       +---------+
	      --       |         |
		 +-----+--+ 1    | yadv1
		 |     | .|      |
	   yadv2 |    0+--+------+        -- BSB1  --
		 |   2    |       --      --        y_offset
		 |        |
   BSB2 --      0+--------+                        --
	--    --

       glyph1: advance height = 6
	       anchor point = (3,1)

       glyph2: advance height = 7
	       anchor point = (9,4)

       TSB is 1 for both glyphs; writing direction is T2B.


	 BSB1     = yadv1 - (TSB1 + ymax1)
	 BSB2     = yadv2 - (TSB2 + ymax2)
	 y_offset = y2 - y1

       vertical advance width of glyph2
	 = y_offset + BSB2 - BSB1
	 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
	 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
	 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1


     b) Assuming that we recompute the advance height of the upper glyph:

				  --      --
		       +---------+        -- TSB1
	--    --       |         |
   TSB2 --       +-----+--+ 1    | yadv1   ymax1
		 |     | .|      |
	   yadv2 |    0+--+------+        --       --
    ymax2        |   2    |       --                y_offset
		 |        |
	--      0+--------+                        --
	      --

       glyph1: advance height = 6
	       anchor point = (3,1)

       glyph2: advance height = 7
	       anchor point = (9,4)

       TSB is 1 for both glyphs; writing direction is T2B.

       y_offset = y2 - y1

       vertical advance width of glyph2
	 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
	 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2


     Comparing a) with b) shows that b) is easier to compute.  I'll wait
     for a reply from Andrei to see what should really be implemented...

     Since horizontal advance widths or vertical advance heights
     can be used alone but not together, no ambiguity occurs.        */

  if ( gpi->last == 0xFFFF )
    goto end;

  /* Get_Anchor() returns HB_Err_Not_Covered if there is no anchor
     table.                                                         */

  error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH(),
		      &entry_x, &entry_y );
  if ( error == HB_Err_Not_Covered )
    goto end;
  if ( error )
    return error;

  if ( gpi->r2l )
  {
    POSITION( buffer->in_pos )->x_advance   = entry_x - gpi->anchor_x;
    POSITION( buffer->in_pos )->new_advance = TRUE;
  }
  else
  {
    POSITION( gpi->last )->x_advance   = gpi->anchor_x - entry_x;
    POSITION( gpi->last )->new_advance = TRUE;
  }

  if ( flags & HB_LOOKUP_FLAG_RIGHT_TO_LEFT )
  {
    POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos;
    POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y;
  }
  else
  {
    POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last;
    POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y;
  }

end:
  error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH(),
		      &exit_x, &exit_y );
  if ( error == HB_Err_Not_Covered )
    gpi->last = 0xFFFF;
  else
  {
    gpi->last     = buffer->in_pos;
    gpi->anchor_x = exit_x;
    gpi->anchor_y = exit_y;
  }
  if ( error )
    return error;

  (buffer->in_pos)++;

  return HB_Err_Ok;
}

Project: harfbuzz

static HB_Error  Lookup_MarkBasePos( GPOS_Instance*    gpi,
				     HB_GPOS_SubTable* st,
				     HB_Buffer        buffer,
				     HB_UShort         flags,
				     HB_UShort         context_length,
				     int               nesting_level )
{
  HB_UShort        i, j, mark_index, base_index, property, class;
  HB_Fixed           x_mark_value, y_mark_value, x_base_value, y_base_value;
  HB_Error         error;
  HB_GPOSHeader*  gpos = gpi->gpos;
  HB_MarkBasePos* mbp = &st->markbase;

  HB_MarkArray*   ma;
  HB_BaseArray*   ba;
  HB_BaseRecord*  br;
  HB_Anchor*      mark_anchor;
  HB_Anchor*      base_anchor;

  HB_Position     o;

  HB_UNUSED(nesting_level);

  if ( context_length != 0xFFFF && context_length < 1 )
    return HB_Err_Not_Covered;

  if ( flags & HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS )
    return HB_Err_Not_Covered;

  if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
		       flags, &property ) )
    return error;

  error = _HB_OPEN_Coverage_Index( &mbp->MarkCoverage, IN_CURGLYPH(),
			  &mark_index );
  if ( error )
    return error;

  /* now we search backwards for a non-mark glyph */

  i = 1;
  j = buffer->in_pos - 1;

  while ( i <= buffer->in_pos )
  {
    error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
					&property );
    if ( error )
      return error;

    if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
      break;

    i++;
    j--;
  }

  /* The following assertion is too strong -- at least for mangal.ttf. */
#if 0
  if ( property != HB_GDEF_BASE_GLYPH )
    return HB_Err_Not_Covered;
#endif

  if ( i > buffer->in_pos )
    return HB_Err_Not_Covered;

  error = _HB_OPEN_Coverage_Index( &mbp->BaseCoverage, IN_GLYPH( j ),
			  &base_index );
  if ( error )
    return error;

  ma = &mbp->MarkArray;

  if ( mark_index >= ma->MarkCount )
    return ERR(HB_Err_Invalid_SubTable);

  class       = ma->MarkRecord[mark_index].Class;
  mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;

  if ( class >= mbp->ClassCount )
    return ERR(HB_Err_Invalid_SubTable);

  ba = &mbp->BaseArray;

  if ( base_index >= ba->BaseCount )
    return ERR(HB_Err_Invalid_SubTable);

  br          = &ba->BaseRecord[base_index];
  base_anchor = &br->BaseAnchor[class];

  error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
		      &x_mark_value, &y_mark_value );
  if ( error )
    return error;

  error = Get_Anchor( gpi, base_anchor, IN_GLYPH( j ),
		      &x_base_value, &y_base_value );
  if ( error )
    return error;

  /* anchor points are not cumulative */

  o = POSITION( buffer->in_pos );

  o->x_pos     = x_base_value - x_mark_value;
  o->y_pos     = y_base_value - y_mark_value;
  o->x_advance = 0;
  o->y_advance = 0;
  o->back      = i;

  (buffer->in_pos)++;

  return HB_Err_Ok;
}

Project: harfbuzz

static HB_Error  Lookup_MarkLigPos( GPOS_Instance*    gpi,
				    HB_GPOS_SubTable* st,
				    HB_Buffer        buffer,
				    HB_UShort         flags,
				    HB_UShort         context_length,
				    int               nesting_level )
{
  HB_UShort        i, j, mark_index, lig_index, property, class;
  HB_UShort        mark_glyph;
  HB_Fixed           x_mark_value, y_mark_value, x_lig_value, y_lig_value;
  HB_Error         error;
  HB_GPOSHeader*  gpos = gpi->gpos;
  HB_MarkLigPos*  mlp = &st->marklig;

  HB_MarkArray*        ma;
  HB_LigatureArray*    la;
  HB_LigatureAttach*   lat;
  HB_ComponentRecord*  cr;
  HB_UShort             comp_index;
  HB_Anchor*           mark_anchor;
  HB_Anchor*           lig_anchor;

  HB_Position    o;

  HB_UNUSED(nesting_level);

  if ( context_length != 0xFFFF && context_length < 1 )
    return HB_Err_Not_Covered;

  if ( flags & HB_LOOKUP_FLAG_IGNORE_LIGATURES )
    return HB_Err_Not_Covered;

  mark_glyph = IN_CURGLYPH();

  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
    return error;

  error = _HB_OPEN_Coverage_Index( &mlp->MarkCoverage, mark_glyph, &mark_index );
  if ( error )
    return error;

  /* now we search backwards for a non-mark glyph */

  i = 1;
  j = buffer->in_pos - 1;

  while ( i <= buffer->in_pos )
  {
    error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
					&property );
    if ( error )
      return error;

    if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
      break;

    i++;
    j--;
  }

  /* Similar to Lookup_MarkBasePos(), I suspect that this assertion is
     too strong, thus it is commented out.                             */
#if 0
  if ( property != HB_GDEF_LIGATURE )
    return HB_Err_Not_Covered;
#endif

  if ( i > buffer->in_pos )
    return HB_Err_Not_Covered;

  error = _HB_OPEN_Coverage_Index( &mlp->LigatureCoverage, IN_GLYPH( j ),
			  &lig_index );
  if ( error )
    return error;

  ma = &mlp->MarkArray;

  if ( mark_index >= ma->MarkCount )
    return ERR(HB_Err_Invalid_SubTable);

  class       = ma->MarkRecord[mark_index].Class;
  mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;

  if ( class >= mlp->ClassCount )
    return ERR(HB_Err_Invalid_SubTable);

  la = &mlp->LigatureArray;

  if ( lig_index >= la->LigatureCount )
    return ERR(HB_Err_Invalid_SubTable);

  lat = &la->LigatureAttach[lig_index];

  /* We must now check whether the ligature ID of the current mark glyph
     is identical to the ligature ID of the found ligature.  If yes, we
     can directly use the component index.  If not, we attach the mark
     glyph to the last component of the ligature.                        */

  if ( IN_LIGID( j ) == IN_LIGID( buffer->in_pos) )
  {
    comp_index = IN_COMPONENT( buffer->in_pos );
    if ( comp_index >= lat->ComponentCount )
      return HB_Err_Not_Covered;
  }
  else
    comp_index = lat->ComponentCount - 1;

  cr         = &lat->ComponentRecord[comp_index];
  lig_anchor = &cr->LigatureAnchor[class];

  error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
		      &x_mark_value, &y_mark_value );
  if ( error )
    return error;
  error = Get_Anchor( gpi, lig_anchor, IN_GLYPH( j ),
		      &x_lig_value, &y_lig_value );
  if ( error )
    return error;

  /* anchor points are not cumulative */

  o = POSITION( buffer->in_pos );

  o->x_pos     = x_lig_value - x_mark_value;
  o->y_pos     = y_lig_value - y_mark_value;
  o->x_advance = 0;
  o->y_advance = 0;
  o->back      = i;

  (buffer->in_pos)++;

  return HB_Err_Ok;
}

Project: harfbuzz

static HB_Error  Lookup_PairPos1( GPOS_Instance*       gpi,
				  HB_PairPosFormat1*  ppf1,
				  HB_Buffer           buffer,
				  HB_UInt              first_pos,
				  HB_UShort            index,
				  HB_UShort            format1,
				  HB_UShort            format2 )
{
  HB_Error              error;
  HB_UShort             numpvr, glyph2;

  HB_PairValueRecord*  pvr;


  if ( index >= ppf1->PairSetCount )
     return ERR(HB_Err_Invalid_SubTable);

  pvr = ppf1->PairSet[index].PairValueRecord;
  if ( !pvr )
    return ERR(HB_Err_Invalid_SubTable);

  glyph2 = IN_CURGLYPH();

  for ( numpvr = ppf1->PairSet[index].PairValueCount;
	numpvr;
	numpvr--, pvr++ )
  {
    if ( glyph2 == pvr->SecondGlyph )
    {
      error = Get_ValueRecord( gpi, &pvr->Value1, format1,
			       POSITION( first_pos ) );
      if ( error )
	return error;
      return Get_ValueRecord( gpi, &pvr->Value2, format2,
			      POSITION( buffer->in_pos ) );
    }
  }

  return HB_Err_Not_Covered;
}

Project: harfbuzz

static HB_Error  Lookup_PairPos2( GPOS_Instance*       gpi,
				  HB_PairPosFormat2*  ppf2,
				  HB_Buffer           buffer,
				  HB_UInt              first_pos,
				  HB_UShort            format1,
				  HB_UShort            format2 )
{
  HB_Error           error;
  HB_UShort          cl1 = 0, cl2 = 0; /* shut compiler up */

  HB_Class1Record*  c1r;
  HB_Class2Record*  c2r;


  error = _HB_OPEN_Get_Class( &ppf2->ClassDef1, IN_GLYPH( first_pos ),
		     &cl1, NULL );
  if ( error && error != HB_Err_Not_Covered )
    return error;
  error = _HB_OPEN_Get_Class( &ppf2->ClassDef2, IN_CURGLYPH(),
		     &cl2, NULL );
  if ( error && error != HB_Err_Not_Covered )
    return error;

  c1r = &ppf2->Class1Record[cl1];
  if ( !c1r )
    return ERR(HB_Err_Invalid_SubTable);
  c2r = &c1r->Class2Record[cl2];

  error = Get_ValueRecord( gpi, &c2r->Value1, format1, POSITION( first_pos ) );
  if ( error )
    return error;
  return Get_ValueRecord( gpi, &c2r->Value2, format2, POSITION( buffer->in_pos ) );
}

Project: harfbuzz

static HB_Error  Lookup_SinglePos( GPOS_Instance*    gpi,
				   HB_GPOS_SubTable* st,
				   HB_Buffer        buffer,
				   HB_UShort         flags,
				   HB_UShort         context_length,
				   int               nesting_level )
{
  HB_UShort        index, property;
  HB_Error         error;
  HB_GPOSHeader*  gpos = gpi->gpos;
  HB_SinglePos*   sp = &st->single;

  HB_UNUSED(nesting_level);

  if ( context_length != 0xFFFF && context_length < 1 )
    return HB_Err_Not_Covered;

  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
    return error;

  error = _HB_OPEN_Coverage_Index( &sp->Coverage, IN_CURGLYPH(), &index );
  if ( error )
    return error;

  switch ( sp->PosFormat )
  {
  case 1:
    error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
			     sp->ValueFormat, POSITION( buffer->in_pos ) );
    if ( error )
      return error;
    break;

  case 2:
    if ( index >= sp->spf.spf2.ValueCount )
      return ERR(HB_Err_Invalid_SubTable);
    error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
			     sp->ValueFormat, POSITION( buffer->in_pos ) );
    if ( error )
      return error;
    break;

  default:
    return ERR(HB_Err_Invalid_SubTable);
  }

  (buffer->in_pos)++;

  return HB_Err_Ok;
}

Project: harfbuzz

static HB_Error  Position_CursiveChain ( HB_Buffer     buffer )
{
  HB_UInt   i, j;
  HB_Position positions = buffer->positions;

  /* First handle all left-to-right connections */
  for (j = 0; j < buffer->in_length; j++)
  {
    if (positions[j].cursive_chain > 0)
      positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
  }

  /* Then handle all right-to-left connections */
  for (i = buffer->in_length; i > 0; i--)
  {
    j = i - 1;

    if (positions[j].cursive_chain < 0)
      positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
  }

  return HB_Err_Ok;
}

Project: harfbuzz

_HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
			HB_Stream         stream,
			HB_UShort         lookup_type )
{
  switch ( lookup_type ) {
    case HB_GPOS_LOOKUP_SINGLE:		return Load_SinglePos		( st, stream );
    case HB_GPOS_LOOKUP_PAIR:		return Load_PairPos		( st, stream );
    case HB_GPOS_LOOKUP_CURSIVE:	return Load_CursivePos		( st, stream );
    case HB_GPOS_LOOKUP_MARKBASE:	return Load_MarkBasePos		( st, stream );
    case HB_GPOS_LOOKUP_MARKLIG:	return Load_MarkLigPos		( st, stream );
    case HB_GPOS_LOOKUP_MARKMARK:	return Load_MarkMarkPos		( st, stream );
    case HB_GPOS_LOOKUP_CONTEXT:	return Load_ContextPos		( st, stream );
    case HB_GPOS_LOOKUP_CHAIN:		return Load_ChainContextPos	( st, stream );
  /*case HB_GPOS_LOOKUP_EXTENSION:	return Load_ExtensionPos	( st, stream );*/
    default:				return ERR(HB_Err_Invalid_SubTable_Format);
  }
}

Project: harfbuzz

static HB_Error  default_mmfunc( HB_Font      font,
				 HB_UShort    metric_id,
				 HB_Fixed*      metric_value,
				 void*        data )
{
  HB_UNUSED(font);
  HB_UNUSED(metric_id);
  HB_UNUSED(metric_value);
  HB_UNUSED(data);
  return ERR(HB_Err_Not_Covered); /* ERR() call intended */
}