- Remove svn:needs-lock, svn:eol-type, and svn:eol-tyle properties.
[reactos.git] / reactos / lib / 3rdparty / freetype / src / otvalid / otvgpos.c
index 16b63bb..ed34705 100644 (file)
-/***************************************************************************/\r
-/*                                                                         */\r
-/*  otvgpos.c                                                              */\r
-/*                                                                         */\r
-/*    OpenType GPOS table validation (body).                               */\r
-/*                                                                         */\r
-/*  Copyright 2002, 2004, 2005, 2006 by                                    */\r
-/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */\r
-/*                                                                         */\r
-/*  This file is part of the FreeType project, and may only be used,       */\r
-/*  modified, and distributed under the terms of the FreeType project      */\r
-/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */\r
-/*  this file you indicate that you have read the license and              */\r
-/*  understand and accept it fully.                                        */\r
-/*                                                                         */\r
-/***************************************************************************/\r
-\r
-\r
-#include "otvalid.h"\r
-#include "otvcommn.h"\r
-#include "otvgpos.h"\r
-\r
-\r
-  /*************************************************************************/\r
-  /*                                                                       */\r
-  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */\r
-  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */\r
-  /* messages during execution.                                            */\r
-  /*                                                                       */\r
-#undef  FT_COMPONENT\r
-#define FT_COMPONENT  trace_otvgpos\r
-\r
-\r
-  static void\r
-  otv_Anchor_validate( FT_Bytes       table,\r
-                       OTV_Validator  valid );\r
-\r
-  static void\r
-  otv_MarkArray_validate( FT_Bytes       table,\r
-                          OTV_Validator  valid );\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                      UTILITY FUNCTIONS                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-#define BaseArrayFunc       otv_x_sxy\r
-#define LigatureAttachFunc  otv_x_sxy\r
-#define Mark2ArrayFunc      otv_x_sxy\r
-\r
-  /* uses valid->extra1 (counter)                             */\r
-  /* uses valid->extra2 (boolean to handle NULL anchor field) */\r
-\r
-  static void\r
-  otv_x_sxy( FT_Bytes       table,\r
-             OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   Count, count1, table_size;\r
-\r
-\r
-    OTV_ENTER;\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-\r
-    Count = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (Count = %d)\n", Count ));\r
-\r
-    OTV_LIMIT_CHECK( Count * valid->extra1 * 2 );\r
-\r
-    table_size = Count * valid->extra1 * 2 + 2;\r
-\r
-    for ( ; Count > 0; Count-- )\r
-      for ( count1 = valid->extra1; count1 > 0; count1-- )\r
-      {\r
-        OTV_OPTIONAL_TABLE( anchor_offset );\r
-\r
-\r
-        OTV_OPTIONAL_OFFSET( anchor_offset );\r
-\r
-        if ( valid->extra2 )\r
-        {\r
-          OTV_SIZE_CHECK( anchor_offset );\r
-          if ( anchor_offset )\r
-            otv_Anchor_validate( table + anchor_offset, valid );\r
-        }\r
-        else\r
-          otv_Anchor_validate( table + anchor_offset, valid );\r
-      }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-#define MarkBasePosFormat1Func  otv_u_O_O_u_O_O\r
-#define MarkLigPosFormat1Func   otv_u_O_O_u_O_O\r
-#define MarkMarkPosFormat1Func  otv_u_O_O_u_O_O\r
-\r
-  /* sets valid->extra1 (class count) */\r
-\r
-  static void\r
-  otv_u_O_O_u_O_O( FT_Bytes       table,\r
-                   OTV_Validator  valid )\r
-  {\r
-    FT_Bytes           p = table;\r
-    FT_UInt            Coverage1, Coverage2, ClassCount;\r
-    FT_UInt            Array1, Array2;\r
-    OTV_Validate_Func  func;\r
-\r
-\r
-    OTV_ENTER;\r
-\r
-    p += 2;     /* skip PosFormat */\r
-\r
-    OTV_LIMIT_CHECK( 10 );\r
-    Coverage1  = FT_NEXT_USHORT( p );\r
-    Coverage2  = FT_NEXT_USHORT( p );\r
-    ClassCount = FT_NEXT_USHORT( p );\r
-    Array1     = FT_NEXT_USHORT( p );\r
-    Array2     = FT_NEXT_USHORT( p );\r
-\r
-    otv_Coverage_validate( table + Coverage1, valid );\r
-    otv_Coverage_validate( table + Coverage2, valid );\r
-\r
-    otv_MarkArray_validate( table + Array1, valid );\r
-\r
-    valid->nesting_level++;\r
-    func          = valid->func[valid->nesting_level];\r
-    valid->extra1 = ClassCount;\r
-\r
-    func( table + Array2, valid );\r
-\r
-    valid->nesting_level--;\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                        VALUE RECORDS                          *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  static FT_UInt\r
-  otv_value_length( FT_UInt  format )\r
-  {\r
-    FT_UInt  count;\r
-\r
-\r
-    count = ( ( format & 0xAA ) >> 1 ) + ( format & 0x55 );\r
-    count = ( ( count  & 0xCC ) >> 2 ) + ( count  & 0x33 );\r
-    count = ( ( count  & 0xF0 ) >> 4 ) + ( count  & 0x0F );\r
-\r
-    return count * 2;\r
-  }\r
-\r
-\r
-  /* uses valid->extra3 (pointer to base table) */\r
-\r
-  static void\r
-  otv_ValueRecord_validate( FT_Bytes       table,\r
-                            FT_UInt        format,\r
-                            OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   count;\r
-\r
-#ifdef FT_DEBUG_LEVEL_TRACE\r
-    FT_Int    loop;\r
-    FT_ULong  res = 0;\r
-\r
-\r
-    OTV_NAME_ENTER( "ValueRecord" );\r
-\r
-    /* display `format' in dual representation */\r
-    for ( loop = 7; loop >= 0; loop-- )\r
-    {\r
-      res <<= 4;\r
-      res  += ( format >> loop ) & 1;\r
-    }\r
-\r
-    OTV_TRACE(( " (format 0b%08lx)\n", res ));\r
-#endif\r
-\r
-    if ( format >= 0x100 )\r
-      FT_INVALID_DATA;\r
-\r
-    for ( count = 4; count > 0; count-- )\r
-    {\r
-      if ( format & 1 )\r
-      {\r
-        /* XPlacement, YPlacement, XAdvance, YAdvance */\r
-        OTV_LIMIT_CHECK( 2 );\r
-        p += 2;\r
-      }\r
-\r
-      format >>= 1;\r
-    }\r
-\r
-    for ( count = 4; count > 0; count-- )\r
-    {\r
-      if ( format & 1 )\r
-      {\r
-        FT_UInt   table_size;\r
-\r
-        OTV_OPTIONAL_TABLE( device );\r
-\r
-\r
-        /* XPlaDevice, YPlaDevice, XAdvDevice, YAdvDevice */\r
-        OTV_LIMIT_CHECK( 2 );\r
-        OTV_OPTIONAL_OFFSET( device );\r
-\r
-        /* XXX: this value is usually too small, especially if the current */\r
-        /* ValueRecord is part of an array -- getting the correct table    */\r
-        /* size is probably not worth the trouble                          */\r
-\r
-        table_size = p - valid->extra3;\r
-\r
-        OTV_SIZE_CHECK( device );\r
-        if ( device )\r
-          otv_Device_validate( valid->extra3 + device, valid );\r
-      }\r
-      format >>= 1;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                           ANCHORS                             *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  static void\r
-  otv_Anchor_validate( FT_Bytes       table,\r
-                       OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   AnchorFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "Anchor");\r
-\r
-    OTV_LIMIT_CHECK( 6 );\r
-    AnchorFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", AnchorFormat ));\r
-\r
-    p += 4;     /* skip XCoordinate and YCoordinate */\r
-\r
-    switch ( AnchorFormat )\r
-    {\r
-    case 1:\r
-      break;\r
-\r
-    case 2:\r
-      OTV_LIMIT_CHECK( 2 );  /* AnchorPoint */\r
-      break;\r
-\r
-    case 3:\r
-      {\r
-        FT_UInt   table_size;\r
-\r
-        OTV_OPTIONAL_TABLE( XDeviceTable );\r
-        OTV_OPTIONAL_TABLE( YDeviceTable );\r
-\r
-\r
-        OTV_LIMIT_CHECK( 4 );\r
-        OTV_OPTIONAL_OFFSET( XDeviceTable );\r
-        OTV_OPTIONAL_OFFSET( YDeviceTable );\r
-\r
-        table_size = 6 + 4;\r
-\r
-        OTV_SIZE_CHECK( XDeviceTable );\r
-        if ( XDeviceTable )\r
-          otv_Device_validate( table + XDeviceTable, valid );\r
-\r
-        OTV_SIZE_CHECK( YDeviceTable );\r
-        if ( YDeviceTable )\r
-          otv_Device_validate( table + YDeviceTable, valid );\r
-      }\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                         MARK ARRAYS                           *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  static void\r
-  otv_MarkArray_validate( FT_Bytes       table,\r
-                          OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   MarkCount;\r
-\r
-\r
-    OTV_NAME_ENTER( "MarkArray" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    MarkCount = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (MarkCount = %d)\n", MarkCount ));\r
-\r
-    OTV_LIMIT_CHECK( MarkCount * 4 );\r
-\r
-    /* MarkRecord */\r
-    for ( ; MarkCount > 0; MarkCount-- )\r
-    {\r
-      p += 2;   /* skip Class */\r
-      /* MarkAnchor */\r
-      otv_Anchor_validate( table + FT_NEXT_USHORT( p ), valid );\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 1                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->extra3 (pointer to base table) */\r
-\r
-  static void\r
-  otv_SinglePos_validate( FT_Bytes       table,\r
-                          OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "SinglePos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    valid->extra3 = table;\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:     /* SinglePosFormat1 */\r
-      {\r
-        FT_UInt  Coverage, ValueFormat;\r
-\r
-\r
-        OTV_LIMIT_CHECK( 4 );\r
-        Coverage    = FT_NEXT_USHORT( p );\r
-        ValueFormat = FT_NEXT_USHORT( p );\r
-\r
-        otv_Coverage_validate( table + Coverage, valid );\r
-        otv_ValueRecord_validate( p, ValueFormat, valid ); /* Value */\r
-      }\r
-      break;\r
-\r
-    case 2:     /* SinglePosFormat2 */\r
-      {\r
-        FT_UInt  Coverage, ValueFormat, ValueCount, len_value;\r
-\r
-\r
-        OTV_LIMIT_CHECK( 6 );\r
-        Coverage    = FT_NEXT_USHORT( p );\r
-        ValueFormat = FT_NEXT_USHORT( p );\r
-        ValueCount  = FT_NEXT_USHORT( p );\r
-\r
-        OTV_TRACE(( " (ValueCount = %d)\n", ValueCount ));\r
-\r
-        len_value = otv_value_length( ValueFormat );\r
-\r
-        otv_Coverage_validate( table + Coverage, valid );\r
-\r
-        OTV_LIMIT_CHECK( ValueCount * len_value );\r
-\r
-        /* Value */\r
-        for ( ; ValueCount > 0; ValueCount-- )\r
-        {\r
-          otv_ValueRecord_validate( p, ValueFormat, valid );\r
-          p += len_value;\r
-        }\r
-      }\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 2                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  static void\r
-  otv_PairSet_validate( FT_Bytes       table,\r
-                        FT_UInt        format1,\r
-                        FT_UInt        format2,\r
-                        OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   value_len1, value_len2, PairValueCount;\r
-\r
-\r
-    OTV_NAME_ENTER( "PairSet" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PairValueCount = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (PairValueCount = %d)\n", PairValueCount ));\r
-\r
-    value_len1 = otv_value_length( format1 );\r
-    value_len2 = otv_value_length( format2 );\r
-\r
-    OTV_LIMIT_CHECK( PairValueCount * ( value_len1 + value_len2 + 2 ) );\r
-\r
-    /* PairValueRecord */\r
-    for ( ; PairValueCount > 0; PairValueCount-- )\r
-    {\r
-      p += 2;       /* skip SecondGlyph */\r
-\r
-      if ( format1 )\r
-        otv_ValueRecord_validate( p, format1, valid ); /* Value1 */\r
-      p += value_len1;\r
-\r
-      if ( format2 )\r
-        otv_ValueRecord_validate( p, format2, valid ); /* Value2 */\r
-      p += value_len2;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /* sets valid->extra3 (pointer to base table) */\r
-\r
-  static void\r
-  otv_PairPos_validate( FT_Bytes       table,\r
-                        OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "PairPos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    valid->extra3 = table;\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:     /* PairPosFormat1 */\r
-      {\r
-        FT_UInt  Coverage, ValueFormat1, ValueFormat2, PairSetCount;\r
-\r
-\r
-        OTV_LIMIT_CHECK( 8 );\r
-        Coverage     = FT_NEXT_USHORT( p );\r
-        ValueFormat1 = FT_NEXT_USHORT( p );\r
-        ValueFormat2 = FT_NEXT_USHORT( p );\r
-        PairSetCount = FT_NEXT_USHORT( p );\r
-\r
-        OTV_TRACE(( " (PairSetCount = %d)\n", PairSetCount ));\r
-\r
-        otv_Coverage_validate( table + Coverage, valid );\r
-\r
-        OTV_LIMIT_CHECK( PairSetCount * 2 );\r
-\r
-        /* PairSetOffset */\r
-        for ( ; PairSetCount > 0; PairSetCount-- )\r
-          otv_PairSet_validate( table + FT_NEXT_USHORT( p ),\r
-                                ValueFormat1, ValueFormat2, valid );\r
-      }\r
-      break;\r
-\r
-    case 2:     /* PairPosFormat2 */\r
-      {\r
-        FT_UInt  Coverage, ValueFormat1, ValueFormat2, ClassDef1, ClassDef2;\r
-        FT_UInt  ClassCount1, ClassCount2, len_value1, len_value2, count;\r
-\r
-\r
-        OTV_LIMIT_CHECK( 14 );\r
-        Coverage     = FT_NEXT_USHORT( p );\r
-        ValueFormat1 = FT_NEXT_USHORT( p );\r
-        ValueFormat2 = FT_NEXT_USHORT( p );\r
-        ClassDef1    = FT_NEXT_USHORT( p );\r
-        ClassDef2    = FT_NEXT_USHORT( p );\r
-        ClassCount1  = FT_NEXT_USHORT( p );\r
-        ClassCount2  = FT_NEXT_USHORT( p );\r
-\r
-        OTV_TRACE(( " (ClassCount1 = %d)\n", ClassCount1 ));\r
-        OTV_TRACE(( " (ClassCount2 = %d)\n", ClassCount2 ));\r
-\r
-        len_value1 = otv_value_length( ValueFormat1 );\r
-        len_value2 = otv_value_length( ValueFormat2 );\r
-\r
-        otv_Coverage_validate( table + Coverage, valid );\r
-        otv_ClassDef_validate( table + ClassDef1, valid );\r
-        otv_ClassDef_validate( table + ClassDef2, valid );\r
-\r
-        OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 *\r
-                     ( len_value1 + len_value2 ) );\r
-\r
-        /* Class1Record */\r
-        for ( ; ClassCount1 > 0; ClassCount1-- )\r
-        {\r
-          /* Class2Record */\r
-          for ( count = ClassCount2; count > 0; count-- )\r
-          {\r
-            if ( ValueFormat1 )\r
-              /* Value1 */\r
-              otv_ValueRecord_validate( p, ValueFormat1, valid );\r
-            p += len_value1;\r
-\r
-            if ( ValueFormat2 )\r
-              /* Value2 */\r
-              otv_ValueRecord_validate( p, ValueFormat2, valid );\r
-            p += len_value2;\r
-          }\r
-        }\r
-      }\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 3                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  static void\r
-  otv_CursivePos_validate( FT_Bytes       table,\r
-                           OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "CursivePos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:     /* CursivePosFormat1 */\r
-      {\r
-        FT_UInt   table_size;\r
-        FT_UInt   Coverage, EntryExitCount;\r
-\r
-        OTV_OPTIONAL_TABLE( EntryAnchor );\r
-        OTV_OPTIONAL_TABLE( ExitAnchor  );\r
-\r
-\r
-        OTV_LIMIT_CHECK( 4 );\r
-        Coverage       = FT_NEXT_USHORT( p );\r
-        EntryExitCount = FT_NEXT_USHORT( p );\r
-\r
-        OTV_TRACE(( " (EntryExitCount = %d)\n", EntryExitCount ));\r
-\r
-        otv_Coverage_validate( table + Coverage, valid );\r
-\r
-        OTV_LIMIT_CHECK( EntryExitCount * 4 );\r
-\r
-        table_size = EntryExitCount * 4 + 4;\r
-\r
-        /* EntryExitRecord */\r
-        for ( ; EntryExitCount > 0; EntryExitCount-- )\r
-        {\r
-          OTV_OPTIONAL_OFFSET( EntryAnchor );\r
-          OTV_OPTIONAL_OFFSET( ExitAnchor  );\r
-\r
-          OTV_SIZE_CHECK( EntryAnchor );\r
-          if ( EntryAnchor )\r
-            otv_Anchor_validate( table + EntryAnchor, valid );\r
-\r
-          OTV_SIZE_CHECK( ExitAnchor );\r
-          if ( ExitAnchor )\r
-            otv_Anchor_validate( table + ExitAnchor, valid );\r
-        }\r
-      }\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 4                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->extra2 (0) */\r
-\r
-  static void\r
-  otv_MarkBasePos_validate( FT_Bytes       table,\r
-                            OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "MarkBasePos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:\r
-      valid->extra2 = 0;\r
-      OTV_NEST2( MarkBasePosFormat1, BaseArray );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 5                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->extra2 (1) */\r
-\r
-  static void\r
-  otv_MarkLigPos_validate( FT_Bytes       table,\r
-                           OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "MarkLigPos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:\r
-      valid->extra2 = 1;\r
-      OTV_NEST3( MarkLigPosFormat1, LigatureArray, LigatureAttach );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 6                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->extra2 (0) */\r
-\r
-  static void\r
-  otv_MarkMarkPos_validate( FT_Bytes       table,\r
-                            OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "MarkMarkPos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:\r
-      valid->extra2 = 0;\r
-      OTV_NEST2( MarkMarkPosFormat1, Mark2Array );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 7                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->extra1 (lookup count) */\r
-\r
-  static void\r
-  otv_ContextPos_validate( FT_Bytes       table,\r
-                           OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "ContextPos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:\r
-      /* no need to check glyph indices/classes used as input for these */\r
-      /* context rules since even invalid glyph indices/classes return  */\r
-      /* meaningful results                                             */\r
-\r
-      valid->extra1 = valid->lookup_count;\r
-      OTV_NEST3( ContextPosFormat1, PosRuleSet, PosRule );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    case 2:\r
-      /* no need to check glyph indices/classes used as input for these */\r
-      /* context rules since even invalid glyph indices/classes return  */\r
-      /* meaningful results                                             */\r
-\r
-      OTV_NEST3( ContextPosFormat2, PosClassSet, PosClassRule );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    case 3:\r
-      OTV_NEST1( ContextPosFormat3 );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 8                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->extra1 (lookup count) */\r
-\r
-  static void\r
-  otv_ChainContextPos_validate( FT_Bytes       table,\r
-                                OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "ChainContextPos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:\r
-      /* no need to check glyph indices/classes used as input for these */\r
-      /* context rules since even invalid glyph indices/classes return  */\r
-      /* meaningful results                                             */\r
-\r
-      valid->extra1 = valid->lookup_count;\r
-      OTV_NEST3( ChainContextPosFormat1,\r
-                 ChainPosRuleSet, ChainPosRule );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    case 2:\r
-      /* no need to check glyph indices/classes used as input for these */\r
-      /* context rules since even invalid glyph indices/classes return  */\r
-      /* meaningful results                                             */\r
-\r
-      OTV_NEST3( ChainContextPosFormat2,\r
-                 ChainPosClassSet, ChainPosClassRule );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    case 3:\r
-      OTV_NEST1( ChainContextPosFormat3 );\r
-      OTV_RUN( table, valid );\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                     GPOS LOOKUP TYPE 9                        *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* uses valid->type_funcs */\r
-\r
-  static void\r
-  otv_ExtensionPos_validate( FT_Bytes       table,\r
-                             OTV_Validator  valid )\r
-  {\r
-    FT_Bytes  p = table;\r
-    FT_UInt   PosFormat;\r
-\r
-\r
-    OTV_NAME_ENTER( "ExtensionPos" );\r
-\r
-    OTV_LIMIT_CHECK( 2 );\r
-    PosFormat = FT_NEXT_USHORT( p );\r
-\r
-    OTV_TRACE(( " (format %d)\n", PosFormat ));\r
-\r
-    switch ( PosFormat )\r
-    {\r
-    case 1:     /* ExtensionPosFormat1 */\r
-      {\r
-        FT_UInt            ExtensionLookupType, ExtensionOffset;\r
-        OTV_Validate_Func  validate;\r
-\r
-\r
-        OTV_LIMIT_CHECK( 6 );\r
-        ExtensionLookupType = FT_NEXT_USHORT( p );\r
-        ExtensionOffset     = FT_NEXT_ULONG( p );\r
-\r
-        if ( ExtensionLookupType == 0 || ExtensionLookupType >= 9 )\r
-          FT_INVALID_DATA;\r
-\r
-        validate = valid->type_funcs[ExtensionLookupType - 1];\r
-        validate( table + ExtensionOffset, valid );\r
-      }\r
-      break;\r
-\r
-    default:\r
-      FT_INVALID_DATA;\r
-    }\r
-\r
-    OTV_EXIT;\r
-  }\r
-\r
-\r
-  static const OTV_Validate_Func  otv_gpos_validate_funcs[9] =\r
-  {\r
-    otv_SinglePos_validate,\r
-    otv_PairPos_validate,\r
-    otv_CursivePos_validate,\r
-    otv_MarkBasePos_validate,\r
-    otv_MarkLigPos_validate,\r
-    otv_MarkMarkPos_validate,\r
-    otv_ContextPos_validate,\r
-    otv_ChainContextPos_validate,\r
-    otv_ExtensionPos_validate\r
-  };\r
-\r
-\r
-  /* sets valid->type_count */\r
-  /* sets valid->type_funcs */\r
-\r
-  FT_LOCAL_DEF( void )\r
-  otv_GPOS_subtable_validate( FT_Bytes       table,\r
-                              OTV_Validator  valid )\r
-  {\r
-    valid->type_count = 9;\r
-    valid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs;\r
-\r
-    otv_Lookup_validate( table, valid );\r
-  }\r
-\r
-\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-  /*****                                                               *****/\r
-  /*****                          GPOS TABLE                           *****/\r
-  /*****                                                               *****/\r
-  /*************************************************************************/\r
-  /*************************************************************************/\r
-\r
-  /* sets valid->glyph_count */\r
-\r
-  FT_LOCAL_DEF( void )\r
-  otv_GPOS_validate( FT_Bytes      table,\r
-                     FT_UInt       glyph_count,\r
-                     FT_Validator  ftvalid )\r
-  {\r
-    OTV_ValidatorRec  validrec;\r
-    OTV_Validator     valid = &validrec;\r
-    FT_Bytes          p     = table;\r
-    FT_UInt           ScriptList, FeatureList, LookupList;\r
-\r
-\r
-    valid->root = ftvalid;\r
-\r
-    FT_TRACE3(( "validating GPOS table\n" ));\r
-    OTV_INIT;\r
-\r
-    OTV_LIMIT_CHECK( 10 );\r
-\r
-    if ( FT_NEXT_ULONG( p ) != 0x10000UL )      /* Version */\r
-      FT_INVALID_DATA;\r
-\r
-    ScriptList  = FT_NEXT_USHORT( p );\r
-    FeatureList = FT_NEXT_USHORT( p );\r
-    LookupList  = FT_NEXT_USHORT( p );\r
-\r
-    valid->type_count  = 9;\r
-    valid->type_funcs  = (OTV_Validate_Func*)otv_gpos_validate_funcs;\r
-    valid->glyph_count = glyph_count;\r
-\r
-    otv_LookupList_validate( table + LookupList,\r
-                             valid );\r
-    otv_FeatureList_validate( table + FeatureList, table + LookupList,\r
-                              valid );\r
-    otv_ScriptList_validate( table + ScriptList, table + FeatureList,\r
-                             valid );\r
-\r
-    FT_TRACE4(( "\n" ));\r
-  }\r
-\r
-\r
-/* END */\r
+/***************************************************************************/
+/*                                                                         */
+/*  otvgpos.c                                                              */
+/*                                                                         */
+/*    OpenType GPOS table validation (body).                               */
+/*                                                                         */
+/*  Copyright 2002, 2004, 2005, 2006 by                                    */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "otvalid.h"
+#include "otvcommn.h"
+#include "otvgpos.h"
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
+  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
+  /* messages during execution.                                            */
+  /*                                                                       */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  trace_otvgpos
+
+
+  static void
+  otv_Anchor_validate( FT_Bytes       table,
+                       OTV_Validator  valid );
+
+  static void
+  otv_MarkArray_validate( FT_Bytes       table,
+                          OTV_Validator  valid );
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                      UTILITY FUNCTIONS                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+#define BaseArrayFunc       otv_x_sxy
+#define LigatureAttachFunc  otv_x_sxy
+#define Mark2ArrayFunc      otv_x_sxy
+
+  /* uses valid->extra1 (counter)                             */
+  /* uses valid->extra2 (boolean to handle NULL anchor field) */
+
+  static void
+  otv_x_sxy( FT_Bytes       table,
+             OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   Count, count1, table_size;
+
+
+    OTV_ENTER;
+
+    OTV_LIMIT_CHECK( 2 );
+
+    Count = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (Count = %d)\n", Count ));
+
+    OTV_LIMIT_CHECK( Count * valid->extra1 * 2 );
+
+    table_size = Count * valid->extra1 * 2 + 2;
+
+    for ( ; Count > 0; Count-- )
+      for ( count1 = valid->extra1; count1 > 0; count1-- )
+      {
+        OTV_OPTIONAL_TABLE( anchor_offset );
+
+
+        OTV_OPTIONAL_OFFSET( anchor_offset );
+
+        if ( valid->extra2 )
+        {
+          OTV_SIZE_CHECK( anchor_offset );
+          if ( anchor_offset )
+            otv_Anchor_validate( table + anchor_offset, valid );
+        }
+        else
+          otv_Anchor_validate( table + anchor_offset, valid );
+      }
+
+    OTV_EXIT;
+  }
+
+
+#define MarkBasePosFormat1Func  otv_u_O_O_u_O_O
+#define MarkLigPosFormat1Func   otv_u_O_O_u_O_O
+#define MarkMarkPosFormat1Func  otv_u_O_O_u_O_O
+
+  /* sets valid->extra1 (class count) */
+
+  static void
+  otv_u_O_O_u_O_O( FT_Bytes       table,
+                   OTV_Validator  valid )
+  {
+    FT_Bytes           p = table;
+    FT_UInt            Coverage1, Coverage2, ClassCount;
+    FT_UInt            Array1, Array2;
+    OTV_Validate_Func  func;
+
+
+    OTV_ENTER;
+
+    p += 2;     /* skip PosFormat */
+
+    OTV_LIMIT_CHECK( 10 );
+    Coverage1  = FT_NEXT_USHORT( p );
+    Coverage2  = FT_NEXT_USHORT( p );
+    ClassCount = FT_NEXT_USHORT( p );
+    Array1     = FT_NEXT_USHORT( p );
+    Array2     = FT_NEXT_USHORT( p );
+
+    otv_Coverage_validate( table + Coverage1, valid );
+    otv_Coverage_validate( table + Coverage2, valid );
+
+    otv_MarkArray_validate( table + Array1, valid );
+
+    valid->nesting_level++;
+    func          = valid->func[valid->nesting_level];
+    valid->extra1 = ClassCount;
+
+    func( table + Array2, valid );
+
+    valid->nesting_level--;
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                        VALUE RECORDS                          *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static FT_UInt
+  otv_value_length( FT_UInt  format )
+  {
+    FT_UInt  count;
+
+
+    count = ( ( format & 0xAA ) >> 1 ) + ( format & 0x55 );
+    count = ( ( count  & 0xCC ) >> 2 ) + ( count  & 0x33 );
+    count = ( ( count  & 0xF0 ) >> 4 ) + ( count  & 0x0F );
+
+    return count * 2;
+  }
+
+
+  /* uses valid->extra3 (pointer to base table) */
+
+  static void
+  otv_ValueRecord_validate( FT_Bytes       table,
+                            FT_UInt        format,
+                            OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   count;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    FT_Int    loop;
+    FT_ULong  res = 0;
+
+
+    OTV_NAME_ENTER( "ValueRecord" );
+
+    /* display `format' in dual representation */
+    for ( loop = 7; loop >= 0; loop-- )
+    {
+      res <<= 4;
+      res  += ( format >> loop ) & 1;
+    }
+
+    OTV_TRACE(( " (format 0b%08lx)\n", res ));
+#endif
+
+    if ( format >= 0x100 )
+      FT_INVALID_DATA;
+
+    for ( count = 4; count > 0; count-- )
+    {
+      if ( format & 1 )
+      {
+        /* XPlacement, YPlacement, XAdvance, YAdvance */
+        OTV_LIMIT_CHECK( 2 );
+        p += 2;
+      }
+
+      format >>= 1;
+    }
+
+    for ( count = 4; count > 0; count-- )
+    {
+      if ( format & 1 )
+      {
+        FT_UInt   table_size;
+
+        OTV_OPTIONAL_TABLE( device );
+
+
+        /* XPlaDevice, YPlaDevice, XAdvDevice, YAdvDevice */
+        OTV_LIMIT_CHECK( 2 );
+        OTV_OPTIONAL_OFFSET( device );
+
+        /* XXX: this value is usually too small, especially if the current */
+        /* ValueRecord is part of an array -- getting the correct table    */
+        /* size is probably not worth the trouble                          */
+
+        table_size = p - valid->extra3;
+
+        OTV_SIZE_CHECK( device );
+        if ( device )
+          otv_Device_validate( valid->extra3 + device, valid );
+      }
+      format >>= 1;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                           ANCHORS                             *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static void
+  otv_Anchor_validate( FT_Bytes       table,
+                       OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   AnchorFormat;
+
+
+    OTV_NAME_ENTER( "Anchor");
+
+    OTV_LIMIT_CHECK( 6 );
+    AnchorFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", AnchorFormat ));
+
+    p += 4;     /* skip XCoordinate and YCoordinate */
+
+    switch ( AnchorFormat )
+    {
+    case 1:
+      break;
+
+    case 2:
+      OTV_LIMIT_CHECK( 2 );  /* AnchorPoint */
+      break;
+
+    case 3:
+      {
+        FT_UInt   table_size;
+
+        OTV_OPTIONAL_TABLE( XDeviceTable );
+        OTV_OPTIONAL_TABLE( YDeviceTable );
+
+
+        OTV_LIMIT_CHECK( 4 );
+        OTV_OPTIONAL_OFFSET( XDeviceTable );
+        OTV_OPTIONAL_OFFSET( YDeviceTable );
+
+        table_size = 6 + 4;
+
+        OTV_SIZE_CHECK( XDeviceTable );
+        if ( XDeviceTable )
+          otv_Device_validate( table + XDeviceTable, valid );
+
+        OTV_SIZE_CHECK( YDeviceTable );
+        if ( YDeviceTable )
+          otv_Device_validate( table + YDeviceTable, valid );
+      }
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                         MARK ARRAYS                           *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static void
+  otv_MarkArray_validate( FT_Bytes       table,
+                          OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   MarkCount;
+
+
+    OTV_NAME_ENTER( "MarkArray" );
+
+    OTV_LIMIT_CHECK( 2 );
+    MarkCount = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (MarkCount = %d)\n", MarkCount ));
+
+    OTV_LIMIT_CHECK( MarkCount * 4 );
+
+    /* MarkRecord */
+    for ( ; MarkCount > 0; MarkCount-- )
+    {
+      p += 2;   /* skip Class */
+      /* MarkAnchor */
+      otv_Anchor_validate( table + FT_NEXT_USHORT( p ), valid );
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 1                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->extra3 (pointer to base table) */
+
+  static void
+  otv_SinglePos_validate( FT_Bytes       table,
+                          OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "SinglePos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    valid->extra3 = table;
+
+    switch ( PosFormat )
+    {
+    case 1:     /* SinglePosFormat1 */
+      {
+        FT_UInt  Coverage, ValueFormat;
+
+
+        OTV_LIMIT_CHECK( 4 );
+        Coverage    = FT_NEXT_USHORT( p );
+        ValueFormat = FT_NEXT_USHORT( p );
+
+        otv_Coverage_validate( table + Coverage, valid );
+        otv_ValueRecord_validate( p, ValueFormat, valid ); /* Value */
+      }
+      break;
+
+    case 2:     /* SinglePosFormat2 */
+      {
+        FT_UInt  Coverage, ValueFormat, ValueCount, len_value;
+
+
+        OTV_LIMIT_CHECK( 6 );
+        Coverage    = FT_NEXT_USHORT( p );
+        ValueFormat = FT_NEXT_USHORT( p );
+        ValueCount  = FT_NEXT_USHORT( p );
+
+        OTV_TRACE(( " (ValueCount = %d)\n", ValueCount ));
+
+        len_value = otv_value_length( ValueFormat );
+
+        otv_Coverage_validate( table + Coverage, valid );
+
+        OTV_LIMIT_CHECK( ValueCount * len_value );
+
+        /* Value */
+        for ( ; ValueCount > 0; ValueCount-- )
+        {
+          otv_ValueRecord_validate( p, ValueFormat, valid );
+          p += len_value;
+        }
+      }
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 2                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static void
+  otv_PairSet_validate( FT_Bytes       table,
+                        FT_UInt        format1,
+                        FT_UInt        format2,
+                        OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   value_len1, value_len2, PairValueCount;
+
+
+    OTV_NAME_ENTER( "PairSet" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PairValueCount = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (PairValueCount = %d)\n", PairValueCount ));
+
+    value_len1 = otv_value_length( format1 );
+    value_len2 = otv_value_length( format2 );
+
+    OTV_LIMIT_CHECK( PairValueCount * ( value_len1 + value_len2 + 2 ) );
+
+    /* PairValueRecord */
+    for ( ; PairValueCount > 0; PairValueCount-- )
+    {
+      p += 2;       /* skip SecondGlyph */
+
+      if ( format1 )
+        otv_ValueRecord_validate( p, format1, valid ); /* Value1 */
+      p += value_len1;
+
+      if ( format2 )
+        otv_ValueRecord_validate( p, format2, valid ); /* Value2 */
+      p += value_len2;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /* sets valid->extra3 (pointer to base table) */
+
+  static void
+  otv_PairPos_validate( FT_Bytes       table,
+                        OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "PairPos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    valid->extra3 = table;
+
+    switch ( PosFormat )
+    {
+    case 1:     /* PairPosFormat1 */
+      {
+        FT_UInt  Coverage, ValueFormat1, ValueFormat2, PairSetCount;
+
+
+        OTV_LIMIT_CHECK( 8 );
+        Coverage     = FT_NEXT_USHORT( p );
+        ValueFormat1 = FT_NEXT_USHORT( p );
+        ValueFormat2 = FT_NEXT_USHORT( p );
+        PairSetCount = FT_NEXT_USHORT( p );
+
+        OTV_TRACE(( " (PairSetCount = %d)\n", PairSetCount ));
+
+        otv_Coverage_validate( table + Coverage, valid );
+
+        OTV_LIMIT_CHECK( PairSetCount * 2 );
+
+        /* PairSetOffset */
+        for ( ; PairSetCount > 0; PairSetCount-- )
+          otv_PairSet_validate( table + FT_NEXT_USHORT( p ),
+                                ValueFormat1, ValueFormat2, valid );
+      }
+      break;
+
+    case 2:     /* PairPosFormat2 */
+      {
+        FT_UInt  Coverage, ValueFormat1, ValueFormat2, ClassDef1, ClassDef2;
+        FT_UInt  ClassCount1, ClassCount2, len_value1, len_value2, count;
+
+
+        OTV_LIMIT_CHECK( 14 );
+        Coverage     = FT_NEXT_USHORT( p );
+        ValueFormat1 = FT_NEXT_USHORT( p );
+        ValueFormat2 = FT_NEXT_USHORT( p );
+        ClassDef1    = FT_NEXT_USHORT( p );
+        ClassDef2    = FT_NEXT_USHORT( p );
+        ClassCount1  = FT_NEXT_USHORT( p );
+        ClassCount2  = FT_NEXT_USHORT( p );
+
+        OTV_TRACE(( " (ClassCount1 = %d)\n", ClassCount1 ));
+        OTV_TRACE(( " (ClassCount2 = %d)\n", ClassCount2 ));
+
+        len_value1 = otv_value_length( ValueFormat1 );
+        len_value2 = otv_value_length( ValueFormat2 );
+
+        otv_Coverage_validate( table + Coverage, valid );
+        otv_ClassDef_validate( table + ClassDef1, valid );
+        otv_ClassDef_validate( table + ClassDef2, valid );
+
+        OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 *
+                     ( len_value1 + len_value2 ) );
+
+        /* Class1Record */
+        for ( ; ClassCount1 > 0; ClassCount1-- )
+        {
+          /* Class2Record */
+          for ( count = ClassCount2; count > 0; count-- )
+          {
+            if ( ValueFormat1 )
+              /* Value1 */
+              otv_ValueRecord_validate( p, ValueFormat1, valid );
+            p += len_value1;
+
+            if ( ValueFormat2 )
+              /* Value2 */
+              otv_ValueRecord_validate( p, ValueFormat2, valid );
+            p += len_value2;
+          }
+        }
+      }
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 3                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static void
+  otv_CursivePos_validate( FT_Bytes       table,
+                           OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "CursivePos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:     /* CursivePosFormat1 */
+      {
+        FT_UInt   table_size;
+        FT_UInt   Coverage, EntryExitCount;
+
+        OTV_OPTIONAL_TABLE( EntryAnchor );
+        OTV_OPTIONAL_TABLE( ExitAnchor  );
+
+
+        OTV_LIMIT_CHECK( 4 );
+        Coverage       = FT_NEXT_USHORT( p );
+        EntryExitCount = FT_NEXT_USHORT( p );
+
+        OTV_TRACE(( " (EntryExitCount = %d)\n", EntryExitCount ));
+
+        otv_Coverage_validate( table + Coverage, valid );
+
+        OTV_LIMIT_CHECK( EntryExitCount * 4 );
+
+        table_size = EntryExitCount * 4 + 4;
+
+        /* EntryExitRecord */
+        for ( ; EntryExitCount > 0; EntryExitCount-- )
+        {
+          OTV_OPTIONAL_OFFSET( EntryAnchor );
+          OTV_OPTIONAL_OFFSET( ExitAnchor  );
+
+          OTV_SIZE_CHECK( EntryAnchor );
+          if ( EntryAnchor )
+            otv_Anchor_validate( table + EntryAnchor, valid );
+
+          OTV_SIZE_CHECK( ExitAnchor );
+          if ( ExitAnchor )
+            otv_Anchor_validate( table + ExitAnchor, valid );
+        }
+      }
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 4                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->extra2 (0) */
+
+  static void
+  otv_MarkBasePos_validate( FT_Bytes       table,
+                            OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "MarkBasePos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:
+      valid->extra2 = 0;
+      OTV_NEST2( MarkBasePosFormat1, BaseArray );
+      OTV_RUN( table, valid );
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 5                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->extra2 (1) */
+
+  static void
+  otv_MarkLigPos_validate( FT_Bytes       table,
+                           OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "MarkLigPos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:
+      valid->extra2 = 1;
+      OTV_NEST3( MarkLigPosFormat1, LigatureArray, LigatureAttach );
+      OTV_RUN( table, valid );
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 6                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->extra2 (0) */
+
+  static void
+  otv_MarkMarkPos_validate( FT_Bytes       table,
+                            OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "MarkMarkPos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:
+      valid->extra2 = 0;
+      OTV_NEST2( MarkMarkPosFormat1, Mark2Array );
+      OTV_RUN( table, valid );
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 7                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->extra1 (lookup count) */
+
+  static void
+  otv_ContextPos_validate( FT_Bytes       table,
+                           OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "ContextPos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:
+      /* no need to check glyph indices/classes used as input for these */
+      /* context rules since even invalid glyph indices/classes return  */
+      /* meaningful results                                             */
+
+      valid->extra1 = valid->lookup_count;
+      OTV_NEST3( ContextPosFormat1, PosRuleSet, PosRule );
+      OTV_RUN( table, valid );
+      break;
+
+    case 2:
+      /* no need to check glyph indices/classes used as input for these */
+      /* context rules since even invalid glyph indices/classes return  */
+      /* meaningful results                                             */
+
+      OTV_NEST3( ContextPosFormat2, PosClassSet, PosClassRule );
+      OTV_RUN( table, valid );
+      break;
+
+    case 3:
+      OTV_NEST1( ContextPosFormat3 );
+      OTV_RUN( table, valid );
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 8                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->extra1 (lookup count) */
+
+  static void
+  otv_ChainContextPos_validate( FT_Bytes       table,
+                                OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "ChainContextPos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:
+      /* no need to check glyph indices/classes used as input for these */
+      /* context rules since even invalid glyph indices/classes return  */
+      /* meaningful results                                             */
+
+      valid->extra1 = valid->lookup_count;
+      OTV_NEST3( ChainContextPosFormat1,
+                 ChainPosRuleSet, ChainPosRule );
+      OTV_RUN( table, valid );
+      break;
+
+    case 2:
+      /* no need to check glyph indices/classes used as input for these */
+      /* context rules since even invalid glyph indices/classes return  */
+      /* meaningful results                                             */
+
+      OTV_NEST3( ChainContextPosFormat2,
+                 ChainPosClassSet, ChainPosClassRule );
+      OTV_RUN( table, valid );
+      break;
+
+    case 3:
+      OTV_NEST1( ChainContextPosFormat3 );
+      OTV_RUN( table, valid );
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GPOS LOOKUP TYPE 9                        *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* uses valid->type_funcs */
+
+  static void
+  otv_ExtensionPos_validate( FT_Bytes       table,
+                             OTV_Validator  valid )
+  {
+    FT_Bytes  p = table;
+    FT_UInt   PosFormat;
+
+
+    OTV_NAME_ENTER( "ExtensionPos" );
+
+    OTV_LIMIT_CHECK( 2 );
+    PosFormat = FT_NEXT_USHORT( p );
+
+    OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+    switch ( PosFormat )
+    {
+    case 1:     /* ExtensionPosFormat1 */
+      {
+        FT_UInt            ExtensionLookupType, ExtensionOffset;
+        OTV_Validate_Func  validate;
+
+
+        OTV_LIMIT_CHECK( 6 );
+        ExtensionLookupType = FT_NEXT_USHORT( p );
+        ExtensionOffset     = FT_NEXT_ULONG( p );
+
+        if ( ExtensionLookupType == 0 || ExtensionLookupType >= 9 )
+          FT_INVALID_DATA;
+
+        validate = valid->type_funcs[ExtensionLookupType - 1];
+        validate( table + ExtensionOffset, valid );
+      }
+      break;
+
+    default:
+      FT_INVALID_DATA;
+    }
+
+    OTV_EXIT;
+  }
+
+
+  static const OTV_Validate_Func  otv_gpos_validate_funcs[9] =
+  {
+    otv_SinglePos_validate,
+    otv_PairPos_validate,
+    otv_CursivePos_validate,
+    otv_MarkBasePos_validate,
+    otv_MarkLigPos_validate,
+    otv_MarkMarkPos_validate,
+    otv_ContextPos_validate,
+    otv_ChainContextPos_validate,
+    otv_ExtensionPos_validate
+  };
+
+
+  /* sets valid->type_count */
+  /* sets valid->type_funcs */
+
+  FT_LOCAL_DEF( void )
+  otv_GPOS_subtable_validate( FT_Bytes       table,
+                              OTV_Validator  valid )
+  {
+    valid->type_count = 9;
+    valid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs;
+
+    otv_Lookup_validate( table, valid );
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                          GPOS TABLE                           *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  /* sets valid->glyph_count */
+
+  FT_LOCAL_DEF( void )
+  otv_GPOS_validate( FT_Bytes      table,
+                     FT_UInt       glyph_count,
+                     FT_Validator  ftvalid )
+  {
+    OTV_ValidatorRec  validrec;
+    OTV_Validator     valid = &validrec;
+    FT_Bytes          p     = table;
+    FT_UInt           ScriptList, FeatureList, LookupList;
+
+
+    valid->root = ftvalid;
+
+    FT_TRACE3(( "validating GPOS table\n" ));
+    OTV_INIT;
+
+    OTV_LIMIT_CHECK( 10 );
+
+    if ( FT_NEXT_ULONG( p ) != 0x10000UL )      /* Version */
+      FT_INVALID_DATA;
+
+    ScriptList  = FT_NEXT_USHORT( p );
+    FeatureList = FT_NEXT_USHORT( p );
+    LookupList  = FT_NEXT_USHORT( p );
+
+    valid->type_count  = 9;
+    valid->type_funcs  = (OTV_Validate_Func*)otv_gpos_validate_funcs;
+    valid->glyph_count = glyph_count;
+
+    otv_LookupList_validate( table + LookupList,
+                             valid );
+    otv_FeatureList_validate( table + FeatureList, table + LookupList,
+                              valid );
+    otv_ScriptList_validate( table + ScriptList, table + FeatureList,
+                             valid );
+
+    FT_TRACE4(( "\n" ));
+  }
+
+
+/* END */