/***************************************************************************/

/*                                                                         */

/*  ttcmap.c                                                               */

/*                                                                         */

/*    TrueType character mapping table (cmap) support (body).              */

/*                                                                         */

/*  Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by      */

/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */

/*                                                                         */

/*  This file is part of the FreeType project, and may only be used,       */

/*  modified, and distributed under the terms of the FreeType project      */

/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */

/*  this file you indicate that you have read the license and              */

/*  understand and accept it fully.                                        */

/*                                                                         */

/***************************************************************************/





#include <ft2build.h>

#include FT_INTERNAL_DEBUG_H



#include "sferrors.h"           /* must come before FT_INTERNAL_VALIDATE_H */



#include FT_INTERNAL_VALIDATE_H

#include FT_INTERNAL_STREAM_H

#include "ttload.h"

#include "ttcmap.h"

#include "sfntpic.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_ttcmap





#define TT_PEEK_SHORT   FT_PEEK_SHORT

#define TT_PEEK_USHORT  FT_PEEK_USHORT

#define TT_PEEK_UINT24  FT_PEEK_UOFF3

#define TT_PEEK_LONG    FT_PEEK_LONG

#define TT_PEEK_ULONG   FT_PEEK_ULONG



#define TT_NEXT_SHORT   FT_NEXT_SHORT

#define TT_NEXT_USHORT  FT_NEXT_USHORT

#define TT_NEXT_UINT24  FT_NEXT_UOFF3

#define TT_NEXT_LONG    FT_NEXT_LONG

#define TT_NEXT_ULONG   FT_NEXT_ULONG





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap_init( TT_CMap   cmap,

                FT_Byte*  table )

  {

    cmap->data = table;

    return SFNT_Err_Ok;

  }





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                           FORMAT 0                            *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME        OFFSET         TYPE          DESCRIPTION                */

  /*                                                                       */

  /*   format      0              USHORT        must be 0                  */

  /*   length      2              USHORT        table length in bytes      */

  /*   language    4              USHORT        Mac language code          */

  /*   glyph_ids   6              BYTE[256]     array of glyph indices     */

  /*               262                                                     */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_0



  FT_CALLBACK_DEF( FT_Error )

  tt_cmap0_validate( FT_Byte*      table,

                     FT_Validator  valid )

  {

    FT_Byte*  p      = table + 2;

    FT_UInt   length = TT_NEXT_USHORT( p );





    if ( table + length > valid->limit || length < 262 )

      FT_INVALID_TOO_SHORT;



    /* check glyph indices whenever necessary */

    if ( valid->level >= FT_VALIDATE_TIGHT )

    {

      FT_UInt  n, idx;





      p = table + 6;

      for ( n = 0; n < 256; n++ )

      {

        idx = *p++;

        if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )

          FT_INVALID_GLYPH_ID;

      }

    }



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap0_char_index( TT_CMap    cmap,

                       FT_UInt32  char_code )

  {

    FT_Byte*  table = cmap->data;





    return char_code < 256 ? table[6 + char_code] : 0;

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap0_char_next( TT_CMap     cmap,

                      FT_UInt32  *pchar_code )

  {

    FT_Byte*   table    = cmap->data;

    FT_UInt32  charcode = *pchar_code;

    FT_UInt32  result   = 0;

    FT_UInt    gindex   = 0;





    table += 6;  /* go to glyph IDs */

    while ( ++charcode < 256 )

    {

      gindex = table[charcode];

      if ( gindex != 0 )

      {

        result = charcode;

        break;

      }

    }



    *pchar_code = result;

    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap0_get_info( TT_CMap       cmap,

                     TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 4;





    cmap_info->format   = 0;

    cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap0_class_rec,

      sizeof ( TT_CMapRec ),



      (FT_CMap_InitFunc)     tt_cmap_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap0_char_index,

      (FT_CMap_CharNextFunc) tt_cmap0_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    0,

    (TT_CMap_ValidateFunc)   tt_cmap0_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap0_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_0 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                          FORMAT 2                             *****/

  /*****                                                               *****/

  /***** This is used for certain CJK encodings that encode text in a  *****/

  /***** mixed 8/16 bits encoding along the following lines:           *****/

  /*****                                                               *****/

  /***** * Certain byte values correspond to an 8-bit character code   *****/

  /*****   (typically in the range 0..127 for ASCII compatibility).    *****/

  /*****                                                               *****/

  /***** * Certain byte values signal the first byte of a 2-byte       *****/

  /*****   character code (but these values are also valid as the      *****/

  /*****   second byte of a 2-byte character).                         *****/

  /*****                                                               *****/

  /***** The following charmap lookup and iteration functions all      *****/

  /***** assume that the value "charcode" correspond to following:     *****/

  /*****                                                               *****/

  /*****   - For one byte characters, "charcode" is simply the         *****/

  /*****     character code.                                           *****/

  /*****                                                               *****/

  /*****   - For two byte characters, "charcode" is the 2-byte         *****/

  /*****     character code in big endian format.  More exactly:       *****/

  /*****                                                               *****/

  /*****       (charcode >> 8)    is the first byte value              *****/

  /*****       (charcode & 0xFF)  is the second byte value             *****/

  /*****                                                               *****/

  /***** Note that not all values of "charcode" are valid according    *****/

  /***** to these rules, and the function moderately check the         *****/

  /***** arguments.                                                    *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME        OFFSET         TYPE            DESCRIPTION              */

  /*                                                                       */

  /*   format      0              USHORT          must be 2                */

  /*   length      2              USHORT          table length in bytes    */

  /*   language    4              USHORT          Mac language code        */

  /*   keys        6              USHORT[256]     sub-header keys          */

  /*   subs        518            SUBHEAD[NSUBS]  sub-headers array        */

  /*   glyph_ids   518+NSUB*8     USHORT[]        glyph ID array           */

  /*                                                                       */

  /* The `keys' table is used to map charcode high-bytes to sub-headers.   */

  /* The value of `NSUBS' is the number of sub-headers defined in the      */

  /* table and is computed by finding the maximum of the `keys' table.     */

  /*                                                                       */

  /* Note that for any n, `keys[n]' is a byte offset within the `subs'     */

  /* table, i.e., it is the corresponding sub-header index multiplied      */

  /* by 8.                                                                 */

  /*                                                                       */

  /* Each sub-header has the following format:                             */

  /*                                                                       */

  /*   NAME        OFFSET      TYPE            DESCRIPTION                 */

  /*                                                                       */

  /*   first       0           USHORT          first valid low-byte        */

  /*   count       2           USHORT          number of valid low-bytes   */

  /*   delta       4           SHORT           see below                   */

  /*   offset      6           USHORT          see below                   */

  /*                                                                       */

  /* A sub-header defines, for each high-byte, the range of valid          */

  /* low-bytes within the charmap.  Note that the range defined by `first' */

  /* and `count' must be completely included in the interval [0..255]      */

  /* according to the specification.                                       */

  /*                                                                       */

  /* If a character code is contained within a given sub-header, then      */

  /* mapping it to a glyph index is done as follows:                       */

  /*                                                                       */

  /* * The value of `offset' is read.  This is a _byte_ distance from the  */

  /*   location of the `offset' field itself into a slice of the           */

  /*   `glyph_ids' table.  Let's call it `slice' (it is a USHORT[] too).   */

  /*                                                                       */

  /* * The value `slice[char.lo - first]' is read.  If it is 0, there is   */

  /*   no glyph for the charcode.  Otherwise, the value of `delta' is      */

  /*   added to it (modulo 65536) to form a new glyph index.               */

  /*                                                                       */

  /* It is up to the validation routine to check that all offsets fall     */

  /* within the glyph IDs table (and not within the `subs' table itself or */

  /* outside of the CMap).                                                 */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_2



  FT_CALLBACK_DEF( FT_Error )

  tt_cmap2_validate( FT_Byte*      table,

                     FT_Validator  valid )

  {

    FT_Byte*  p      = table + 2;           /* skip format */

    FT_UInt   length = TT_PEEK_USHORT( p );

    FT_UInt   n, max_subs;

    FT_Byte*  keys;                         /* keys table */

    FT_Byte*  subs;                         /* sub-headers */

    FT_Byte*  glyph_ids;                    /* glyph ID array */





    if ( table + length > valid->limit || length < 6 + 512 )

      FT_INVALID_TOO_SHORT;



    keys = table + 6;



    /* parse keys to compute sub-headers count */

    p        = keys;

    max_subs = 0;

    for ( n = 0; n < 256; n++ )

    {

      FT_UInt  idx = TT_NEXT_USHORT( p );





      /* value must be multiple of 8 */

      if ( valid->level >= FT_VALIDATE_PARANOID && ( idx & 7 ) != 0 )

        FT_INVALID_DATA;



      idx >>= 3;



      if ( idx > max_subs )

        max_subs = idx;

    }



    FT_ASSERT( p == table + 518 );



    subs      = p;

    glyph_ids = subs + (max_subs + 1) * 8;

    if ( glyph_ids > valid->limit )

      FT_INVALID_TOO_SHORT;



    /* parse sub-headers */

    for ( n = 0; n <= max_subs; n++ )

    {

      FT_UInt   first_code, code_count, offset;

      FT_Int    delta;

      FT_Byte*  ids;





      first_code = TT_NEXT_USHORT( p );

      code_count = TT_NEXT_USHORT( p );

      delta      = TT_NEXT_SHORT( p );

      offset     = TT_NEXT_USHORT( p );



      /* many Dynalab fonts have empty sub-headers */

      if ( code_count == 0 )

        continue;



      /* check range within 0..255 */

      if ( valid->level >= FT_VALIDATE_PARANOID )

      {

        if ( first_code >= 256 || first_code + code_count > 256 )

          FT_INVALID_DATA;

      }



      /* check offset */

      if ( offset != 0 )

      {

        ids = p - 2 + offset;

        if ( ids < glyph_ids || ids + code_count*2 > table + length )

          FT_INVALID_OFFSET;



        /* check glyph IDs */

        if ( valid->level >= FT_VALIDATE_TIGHT )

        {

          FT_Byte*  limit = p + code_count * 2;

          FT_UInt   idx;





          for ( ; p < limit; )

          {

            idx = TT_NEXT_USHORT( p );

            if ( idx != 0 )

            {

              idx = ( idx + delta ) & 0xFFFFU;

              if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )

                FT_INVALID_GLYPH_ID;

            }

          }

        }

      }

    }



    return SFNT_Err_Ok;

  }





  /* return sub header corresponding to a given character code */

  /* NULL on invalid charcode                                  */

  static FT_Byte*

  tt_cmap2_get_subheader( FT_Byte*   table,

                          FT_UInt32  char_code )

  {

    FT_Byte*  result = NULL;





    if ( char_code < 0x10000UL )

    {

      FT_UInt   char_lo = (FT_UInt)( char_code & 0xFF );

      FT_UInt   char_hi = (FT_UInt)( char_code >> 8 );

      FT_Byte*  p       = table + 6;    /* keys table */

      FT_Byte*  subs    = table + 518;  /* subheaders table */

      FT_Byte*  sub;





      if ( char_hi == 0 )

      {

        /* an 8-bit character code -- we use subHeader 0 in this case */

        /* to test whether the character code is in the charmap       */

        /*                                                            */

        sub = subs;  /* jump to first sub-header */



        /* check that the sub-header for this byte is 0, which */

        /* indicates that it is really a valid one-byte value  */

        /* Otherwise, return 0                                 */

        /*                                                     */

        p += char_lo * 2;

        if ( TT_PEEK_USHORT( p ) != 0 )

          goto Exit;

      }

      else

      {

        /* a 16-bit character code */



        /* jump to key entry  */

        p  += char_hi * 2;

        /* jump to sub-header */

        sub = subs + ( FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 8 ) );



        /* check that the high byte isn't a valid one-byte value */

        if ( sub == subs )

          goto Exit;

      }

      result = sub;

    }

  Exit:

    return result;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap2_char_index( TT_CMap    cmap,

                       FT_UInt32  char_code )

  {

    FT_Byte*  table   = cmap->data;

    FT_UInt   result  = 0;

    FT_Byte*  subheader;





    subheader = tt_cmap2_get_subheader( table, char_code );

    if ( subheader )

    {

      FT_Byte*  p   = subheader;

      FT_UInt   idx = (FT_UInt)(char_code & 0xFF);

      FT_UInt   start, count;

      FT_Int    delta;

      FT_UInt   offset;





      start  = TT_NEXT_USHORT( p );

      count  = TT_NEXT_USHORT( p );

      delta  = TT_NEXT_SHORT ( p );

      offset = TT_PEEK_USHORT( p );



      idx -= start;

      if ( idx < count && offset != 0 )

      {

        p  += offset + 2 * idx;

        idx = TT_PEEK_USHORT( p );



        if ( idx != 0 )

          result = (FT_UInt)( idx + delta ) & 0xFFFFU;

      }

    }

    return result;

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap2_char_next( TT_CMap     cmap,

                      FT_UInt32  *pcharcode )

  {

    FT_Byte*   table    = cmap->data;

    FT_UInt    gindex   = 0;

    FT_UInt32  result   = 0;

    FT_UInt32  charcode = *pcharcode + 1;

    FT_Byte*   subheader;





    while ( charcode < 0x10000UL )

    {

      subheader = tt_cmap2_get_subheader( table, charcode );

      if ( subheader )

      {

        FT_Byte*  p       = subheader;

        FT_UInt   start   = TT_NEXT_USHORT( p );

        FT_UInt   count   = TT_NEXT_USHORT( p );

        FT_Int    delta   = TT_NEXT_SHORT ( p );

        FT_UInt   offset  = TT_PEEK_USHORT( p );

        FT_UInt   char_lo = (FT_UInt)( charcode & 0xFF );

        FT_UInt   pos, idx;





        if ( offset == 0 )

          goto Next_SubHeader;



        if ( char_lo < start )

        {

          char_lo = start;

          pos     = 0;

        }

        else

          pos = (FT_UInt)( char_lo - start );



        p       += offset + pos * 2;

        charcode = FT_PAD_FLOOR( charcode, 256 ) + char_lo;



        for ( ; pos < count; pos++, charcode++ )

        {

          idx = TT_NEXT_USHORT( p );



          if ( idx != 0 )

          {

            gindex = ( idx + delta ) & 0xFFFFU;

            if ( gindex != 0 )

            {

              result = charcode;

              goto Exit;

            }

          }

        }

      }



      /* jump to next sub-header, i.e. higher byte value */

    Next_SubHeader:

      charcode = FT_PAD_FLOOR( charcode, 256 ) + 256;

    }



  Exit:

    *pcharcode = result;



    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap2_get_info( TT_CMap       cmap,

                     TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 4;





    cmap_info->format   = 2;

    cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap2_class_rec,

      sizeof ( TT_CMapRec ),



      (FT_CMap_InitFunc)     tt_cmap_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap2_char_index,

      (FT_CMap_CharNextFunc) tt_cmap2_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    2,

    (TT_CMap_ValidateFunc)   tt_cmap2_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap2_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_2 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                           FORMAT 4                            *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME          OFFSET         TYPE              DESCRIPTION          */

  /*                                                                       */

  /*   format        0              USHORT            must be 4            */

  /*   length        2              USHORT            table length         */

  /*                                                  in bytes             */

  /*   language      4              USHORT            Mac language code    */

  /*                                                                       */

  /*   segCountX2    6              USHORT            2*NUM_SEGS           */

  /*   searchRange   8              USHORT            2*(1 << LOG_SEGS)    */

  /*   entrySelector 10             USHORT            LOG_SEGS             */

  /*   rangeShift    12             USHORT            segCountX2 -         */

  /*                                                    searchRange        */

  /*                                                                       */

  /*   endCount      14             USHORT[NUM_SEGS]  end charcode for     */

  /*                                                  each segment; last   */

  /*                                                  is 0xFFFF            */

  /*                                                                       */

  /*   pad           14+NUM_SEGS*2  USHORT            padding              */

  /*                                                                       */

  /*   startCount    16+NUM_SEGS*2  USHORT[NUM_SEGS]  first charcode for   */

  /*                                                  each segment         */

  /*                                                                       */

  /*   idDelta       16+NUM_SEGS*4  SHORT[NUM_SEGS]   delta for each       */

  /*                                                  segment              */

  /*   idOffset      16+NUM_SEGS*6  SHORT[NUM_SEGS]   range offset for     */

  /*                                                  each segment; can be */

  /*                                                  zero                 */

  /*                                                                       */

  /*   glyphIds      16+NUM_SEGS*8  USHORT[]          array of glyph ID    */

  /*                                                  ranges               */

  /*                                                                       */

  /* Character codes are modelled by a series of ordered (increasing)      */

  /* intervals called segments.  Each segment has start and end codes,     */

  /* provided by the `startCount' and `endCount' arrays.  Segments must    */

  /* not overlap, and the last segment should always contain the value     */

  /* 0xFFFF for `endCount'.                                                */

  /*                                                                       */

  /* The fields `searchRange', `entrySelector' and `rangeShift' are better */

  /* ignored (they are traces of over-engineering in the TrueType          */

  /* specification).                                                       */

  /*                                                                       */

  /* Each segment also has a signed `delta', as well as an optional offset */

  /* within the `glyphIds' table.                                          */

  /*                                                                       */

  /* If a segment's idOffset is 0, the glyph index corresponding to any    */

  /* charcode within the segment is obtained by adding the value of        */

  /* `idDelta' directly to the charcode, modulo 65536.                     */

  /*                                                                       */

  /* Otherwise, a glyph index is taken from the glyph IDs sub-array for    */

  /* the segment, and the value of `idDelta' is added to it.               */

  /*                                                                       */

  /*                                                                       */

  /* Finally, note that a lot of fonts contain an invalid last segment,    */

  /* where `start' and `end' are correctly set to 0xFFFF but both `delta'  */

  /* and `offset' are incorrect (e.g., `opens___.ttf' which comes with     */

  /* OpenOffice.org).  We need special code to deal with them correctly.   */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_4



  typedef struct  TT_CMap4Rec_

  {

    TT_CMapRec  cmap;

    FT_UInt32   cur_charcode;   /* current charcode */

    FT_UInt     cur_gindex;     /* current glyph index */



    FT_UInt     num_ranges;

    FT_UInt     cur_range;

    FT_UInt     cur_start;

    FT_UInt     cur_end;

    FT_Int      cur_delta;

    FT_Byte*    cur_values;



  } TT_CMap4Rec, *TT_CMap4;





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap4_init( TT_CMap4  cmap,

                 FT_Byte*  table )

  {

    FT_Byte*  p;





    cmap->cmap.data    = table;



    p                  = table + 6;

    cmap->num_ranges   = FT_PEEK_USHORT( p ) >> 1;

    cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;

    cmap->cur_gindex   = 0;



    return SFNT_Err_Ok;

  }





  static FT_Int

  tt_cmap4_set_range( TT_CMap4  cmap,

                      FT_UInt   range_index )

  {

    FT_Byte*  table = cmap->cmap.data;

    FT_Byte*  p;

    FT_UInt   num_ranges = cmap->num_ranges;





    while ( range_index < num_ranges )

    {

      FT_UInt  offset;





      p             = table + 14 + range_index * 2;

      cmap->cur_end = FT_PEEK_USHORT( p );



      p              += 2 + num_ranges * 2;

      cmap->cur_start = FT_PEEK_USHORT( p );



      p              += num_ranges * 2;

      cmap->cur_delta = FT_PEEK_SHORT( p );



      p     += num_ranges * 2;

      offset = FT_PEEK_USHORT( p );



      /* some fonts have an incorrect last segment; */

      /* we have to catch it                        */

      if ( range_index     >= num_ranges - 1 &&

           cmap->cur_start == 0xFFFFU        &&

           cmap->cur_end   == 0xFFFFU        )

      {

        TT_Face   face  = (TT_Face)cmap->cmap.cmap.charmap.face;

        FT_Byte*  limit = face->cmap_table + face->cmap_size;





        if ( offset && p + offset + 2 > limit )

        {

          cmap->cur_delta = 1;

          offset          = 0;

        }

      }



      if ( offset != 0xFFFFU )

      {

        cmap->cur_values = offset ? p + offset : NULL;

        cmap->cur_range  = range_index;

        return 0;

      }



      /* we skip empty segments */

      range_index++;

    }



    return -1;

  }





  /* search the index of the charcode next to cmap->cur_charcode; */

  /* caller should call tt_cmap4_set_range with proper range      */

  /* before calling this function                                 */

  /*                                                              */

  static void

  tt_cmap4_next( TT_CMap4  cmap )

  {

    FT_UInt  charcode;





    if ( cmap->cur_charcode >= 0xFFFFUL )

      goto Fail;



    charcode = (FT_UInt)cmap->cur_charcode + 1;



    if ( charcode < cmap->cur_start )

      charcode = cmap->cur_start;



    for ( ;; )

    {

      FT_Byte*  values = cmap->cur_values;

      FT_UInt   end    = cmap->cur_end;

      FT_Int    delta  = cmap->cur_delta;





      if ( charcode <= end )

      {

        if ( values )

        {

          FT_Byte*  p = values + 2 * ( charcode - cmap->cur_start );





          do

          {

            FT_UInt  gindex = FT_NEXT_USHORT( p );





            if ( gindex != 0 )

            {

              gindex = (FT_UInt)( ( gindex + delta ) & 0xFFFFU );

              if ( gindex != 0 )

              {

                cmap->cur_charcode = charcode;

                cmap->cur_gindex   = gindex;

                return;

              }

            }

          } while ( ++charcode <= end );

        }

        else

        {

          do

          {

            FT_UInt  gindex = (FT_UInt)( ( charcode + delta ) & 0xFFFFU );





            if ( gindex != 0 )

            {

              cmap->cur_charcode = charcode;

              cmap->cur_gindex   = gindex;

              return;

            }

          } while ( ++charcode <= end );

        }

      }



      /* we need to find another range */

      if ( tt_cmap4_set_range( cmap, cmap->cur_range + 1 ) < 0 )

        break;



      if ( charcode < cmap->cur_start )

        charcode = cmap->cur_start;

    }



  Fail:

    cmap->cur_charcode = (FT_UInt32)0xFFFFFFFFUL;

    cmap->cur_gindex   = 0;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap4_validate( FT_Byte*      table,

                     FT_Validator  valid )

  {

    FT_Byte*  p      = table + 2;               /* skip format */

    FT_UInt   length = TT_NEXT_USHORT( p );

    FT_Byte   *ends, *starts, *offsets, *deltas, *glyph_ids;

    FT_UInt   num_segs;

    FT_Error  error = SFNT_Err_Ok;





    if ( length < 16 )

      FT_INVALID_TOO_SHORT;



    /* in certain fonts, the `length' field is invalid and goes */

    /* out of bound.  We try to correct this here...            */

    if ( table + length > valid->limit )

    {

      if ( valid->level >= FT_VALIDATE_TIGHT )

        FT_INVALID_TOO_SHORT;



      length = (FT_UInt)( valid->limit - table );

    }



    p        = table + 6;

    num_segs = TT_NEXT_USHORT( p );   /* read segCountX2 */



    if ( valid->level >= FT_VALIDATE_PARANOID )

    {

      /* check that we have an even value here */

      if ( num_segs & 1 )

        FT_INVALID_DATA;

    }



    num_segs /= 2;



    if ( length < 16 + num_segs * 2 * 4 )

      FT_INVALID_TOO_SHORT;



    /* check the search parameters - even though we never use them */

    /*                                                             */

    if ( valid->level >= FT_VALIDATE_PARANOID )

    {

      /* check the values of `searchRange', `entrySelector', `rangeShift' */

      FT_UInt  search_range   = TT_NEXT_USHORT( p );

      FT_UInt  entry_selector = TT_NEXT_USHORT( p );

      FT_UInt  range_shift    = TT_NEXT_USHORT( p );





      if ( ( search_range | range_shift ) & 1 )  /* must be even values */

        FT_INVALID_DATA;



      search_range /= 2;

      range_shift  /= 2;



      /* `search range' is the greatest power of 2 that is <= num_segs */



      if ( search_range                > num_segs                 ||

           search_range * 2            < num_segs                 ||

           search_range + range_shift != num_segs                 ||

           search_range               != ( 1U << entry_selector ) )

        FT_INVALID_DATA;

    }



    ends      = table   + 14;

    starts    = table   + 16 + num_segs * 2;

    deltas    = starts  + num_segs * 2;

    offsets   = deltas  + num_segs * 2;

    glyph_ids = offsets + num_segs * 2;



    /* check last segment; its end count value must be 0xFFFF */

    if ( valid->level >= FT_VALIDATE_PARANOID )

    {

      p = ends + ( num_segs - 1 ) * 2;

      if ( TT_PEEK_USHORT( p ) != 0xFFFFU )

        FT_INVALID_DATA;

    }



    {

      FT_UInt   start, end, offset, n;

      FT_UInt   last_start = 0, last_end = 0;

      FT_Int    delta;

      FT_Byte*  p_start   = starts;

      FT_Byte*  p_end     = ends;

      FT_Byte*  p_delta   = deltas;

      FT_Byte*  p_offset  = offsets;





      for ( n = 0; n < num_segs; n++ )

      {

        p      = p_offset;

        start  = TT_NEXT_USHORT( p_start );

        end    = TT_NEXT_USHORT( p_end );

        delta  = TT_NEXT_SHORT( p_delta );

        offset = TT_NEXT_USHORT( p_offset );



        if ( start > end )

          FT_INVALID_DATA;



        /* this test should be performed at default validation level; */

        /* unfortunately, some popular Asian fonts have overlapping   */

        /* ranges in their charmaps                                   */

        /*                                                            */

        if ( start <= last_end && n > 0 )

        {

          if ( valid->level >= FT_VALIDATE_TIGHT )

            FT_INVALID_DATA;

          else

          {

            /* allow overlapping segments, provided their start points */

            /* and end points, respectively, are in ascending order    */

            /*                                                         */

            if ( last_start > start || last_end > end )

              error |= TT_CMAP_FLAG_UNSORTED;

            else

              error |= TT_CMAP_FLAG_OVERLAPPING;

          }

        }



        if ( offset && offset != 0xFFFFU )

        {

          p += offset;  /* start of glyph ID array */



          /* check that we point within the glyph IDs table only */

          if ( valid->level >= FT_VALIDATE_TIGHT )

          {

            if ( p < glyph_ids                                ||

                 p + ( end - start + 1 ) * 2 > table + length )

              FT_INVALID_DATA;

          }

          /* Some fonts handle the last segment incorrectly.  In */

          /* theory, 0xFFFF might point to an ordinary glyph --  */

          /* a cmap 4 is versatile and could be used for any     */

          /* encoding, not only Unicode.  However, reality shows */

          /* that far too many fonts are sloppy and incorrectly  */

          /* set all fields but `start' and `end' for the last   */

          /* segment if it contains only a single character.     */

          /*                                                     */

          /* We thus omit the test here, delaying it to the      */

          /* routines which actually access the cmap.            */

          else if ( n != num_segs - 1                       ||

                    !( start == 0xFFFFU && end == 0xFFFFU ) )

          {

            if ( p < glyph_ids                              ||

                 p + ( end - start + 1 ) * 2 > valid->limit )

              FT_INVALID_DATA;

          }



          /* check glyph indices within the segment range */

          if ( valid->level >= FT_VALIDATE_TIGHT )

          {

            FT_UInt  i, idx;





            for ( i = start; i < end; i++ )

            {

              idx = FT_NEXT_USHORT( p );

              if ( idx != 0 )

              {

                idx = (FT_UInt)( idx + delta ) & 0xFFFFU;



                if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )

                  FT_INVALID_GLYPH_ID;

              }

            }

          }

        }

        else if ( offset == 0xFFFFU )

        {

          /* some fonts (erroneously?) use a range offset of 0xFFFF */

          /* to mean missing glyph in cmap table                    */

          /*                                                        */

          if ( valid->level >= FT_VALIDATE_PARANOID    ||

               n != num_segs - 1                       ||

               !( start == 0xFFFFU && end == 0xFFFFU ) )

            FT_INVALID_DATA;

        }



        last_start = start;

        last_end   = end;

      }

    }



    return error;

  }





  static FT_UInt

  tt_cmap4_char_map_linear( TT_CMap     cmap,

                            FT_UInt32*  pcharcode,

                            FT_Bool     next )

  {

    FT_UInt    num_segs2, start, end, offset;

    FT_Int     delta;

    FT_UInt    i, num_segs;

    FT_UInt32  charcode = *pcharcode;

    FT_UInt    gindex   = 0;

    FT_Byte*   p;





    p = cmap->data + 6;

    num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );



    num_segs = num_segs2 >> 1;



    if ( !num_segs )

      return 0;



    if ( next )

      charcode++;



    /* linear search */

    for ( ; charcode <= 0xFFFFU; charcode++ )

    {

      FT_Byte*  q;





      p = cmap->data + 14;               /* ends table   */

      q = cmap->data + 16 + num_segs2;   /* starts table */



      for ( i = 0; i < num_segs; i++ )

      {

        end   = TT_NEXT_USHORT( p );

        start = TT_NEXT_USHORT( q );



        if ( charcode >= start && charcode <= end )

        {

          p       = q - 2 + num_segs2;

          delta   = TT_PEEK_SHORT( p );

          p      += num_segs2;

          offset  = TT_PEEK_USHORT( p );



          /* some fonts have an incorrect last segment; */

          /* we have to catch it                        */

          if ( i >= num_segs - 1                  &&

               start == 0xFFFFU && end == 0xFFFFU )

          {

            TT_Face   face  = (TT_Face)cmap->cmap.charmap.face;

            FT_Byte*  limit = face->cmap_table + face->cmap_size;





            if ( offset && p + offset + 2 > limit )

            {

              delta  = 1;

              offset = 0;

            }

          }



          if ( offset == 0xFFFFU )

            continue;



          if ( offset )

          {

            p += offset + ( charcode - start ) * 2;

            gindex = TT_PEEK_USHORT( p );

            if ( gindex != 0 )

              gindex = (FT_UInt)( gindex + delta ) & 0xFFFFU;

          }

          else

            gindex = (FT_UInt)( charcode + delta ) & 0xFFFFU;



          break;

        }

      }



      if ( !next || gindex )

        break;

    }



    if ( next && gindex )

      *pcharcode = charcode;



    return gindex;

  }





  static FT_UInt

  tt_cmap4_char_map_binary( TT_CMap     cmap,

                            FT_UInt32*  pcharcode,

                            FT_Bool     next )

  {

    FT_UInt   num_segs2, start, end, offset;

    FT_Int    delta;

    FT_UInt   max, min, mid, num_segs;

    FT_UInt   charcode = (FT_UInt)*pcharcode;

    FT_UInt   gindex   = 0;

    FT_Byte*  p;





    p = cmap->data + 6;

    num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );



    if ( !num_segs2 )

      return 0;



    num_segs = num_segs2 >> 1;



    /* make compiler happy */

    mid = num_segs;

    end = 0xFFFFU;



    if ( next )

      charcode++;



    min = 0;

    max = num_segs;



    /* binary search */

    while ( min < max )

    {

      mid    = ( min + max ) >> 1;

      p      = cmap->data + 14 + mid * 2;

      end    = TT_PEEK_USHORT( p );

      p     += 2 + num_segs2;

      start  = TT_PEEK_USHORT( p );



      if ( charcode < start )

        max = mid;

      else if ( charcode > end )

        min = mid + 1;

      else

      {

        p     += num_segs2;

        delta  = TT_PEEK_SHORT( p );

        p     += num_segs2;

        offset = TT_PEEK_USHORT( p );



        /* some fonts have an incorrect last segment; */

        /* we have to catch it                        */

        if ( mid >= num_segs - 1                &&

             start == 0xFFFFU && end == 0xFFFFU )

        {

          TT_Face   face  = (TT_Face)cmap->cmap.charmap.face;

          FT_Byte*  limit = face->cmap_table + face->cmap_size;





          if ( offset && p + offset + 2 > limit )

          {

            delta  = 1;

            offset = 0;

          }

        }



        /* search the first segment containing `charcode' */

        if ( cmap->flags & TT_CMAP_FLAG_OVERLAPPING )

        {

          FT_UInt  i;





          /* call the current segment `max' */

          max = mid;



          if ( offset == 0xFFFFU )

            mid = max + 1;



          /* search in segments before the current segment */

          for ( i = max ; i > 0; i-- )

          {

            FT_UInt   prev_end;

            FT_Byte*  old_p;





            old_p    = p;

            p        = cmap->data + 14 + ( i - 1 ) * 2;

            prev_end = TT_PEEK_USHORT( p );



            if ( charcode > prev_end )

            {

              p = old_p;

              break;

            }



            end    = prev_end;

            p     += 2 + num_segs2;

            start  = TT_PEEK_USHORT( p );

            p     += num_segs2;

            delta  = TT_PEEK_SHORT( p );

            p     += num_segs2;

            offset = TT_PEEK_USHORT( p );



            if ( offset != 0xFFFFU )

              mid = i - 1;

          }



          /* no luck */

          if ( mid == max + 1 )

          {

            if ( i != max )

            {

              p      = cmap->data + 14 + max * 2;

              end    = TT_PEEK_USHORT( p );

              p     += 2 + num_segs2;

              start  = TT_PEEK_USHORT( p );

              p     += num_segs2;

              delta  = TT_PEEK_SHORT( p );

              p     += num_segs2;

              offset = TT_PEEK_USHORT( p );

            }



            mid = max;



            /* search in segments after the current segment */

            for ( i = max + 1; i < num_segs; i++ )

            {

              FT_UInt  next_end, next_start;





              p          = cmap->data + 14 + i * 2;

              next_end   = TT_PEEK_USHORT( p );

              p         += 2 + num_segs2;

              next_start = TT_PEEK_USHORT( p );



              if ( charcode < next_start )

                break;



              end    = next_end;

              start  = next_start;

              p     += num_segs2;

              delta  = TT_PEEK_SHORT( p );

              p     += num_segs2;

              offset = TT_PEEK_USHORT( p );



              if ( offset != 0xFFFFU )

                mid = i;

            }

            i--;



            /* still no luck */

            if ( mid == max )

            {

              mid = i;



              break;

            }

          }



          /* end, start, delta, and offset are for the i'th segment */

          if ( mid != i )

          {

            p      = cmap->data + 14 + mid * 2;

            end    = TT_PEEK_USHORT( p );

            p     += 2 + num_segs2;

            start  = TT_PEEK_USHORT( p );

            p     += num_segs2;

            delta  = TT_PEEK_SHORT( p );

            p     += num_segs2;

            offset = TT_PEEK_USHORT( p );

          }

        }

        else

        {

          if ( offset == 0xFFFFU )

            break;

        }



        if ( offset )

        {

          p += offset + ( charcode - start ) * 2;

          gindex = TT_PEEK_USHORT( p );

          if ( gindex != 0 )

            gindex = (FT_UInt)( gindex + delta ) & 0xFFFFU;

        }

        else

          gindex = (FT_UInt)( charcode + delta ) & 0xFFFFU;



        break;

      }

    }



    if ( next )

    {

      TT_CMap4  cmap4 = (TT_CMap4)cmap;





      /* if `charcode' is not in any segment, then `mid' is */

      /* the segment nearest to `charcode'                  */

      /*                                                    */



      if ( charcode > end )

      {

        mid++;

        if ( mid == num_segs )

          return 0;

      }



      if ( tt_cmap4_set_range( cmap4, mid ) )

      {

        if ( gindex )

          *pcharcode = charcode;

      }

      else

      {

        cmap4->cur_charcode = charcode;



        if ( gindex )

          cmap4->cur_gindex = gindex;

        else

        {

          cmap4->cur_charcode = charcode;

          tt_cmap4_next( cmap4 );

          gindex = cmap4->cur_gindex;

        }



        if ( gindex )

          *pcharcode = cmap4->cur_charcode;

      }

    }



    return gindex;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap4_char_index( TT_CMap    cmap,

                       FT_UInt32  char_code )

  {

    if ( char_code >= 0x10000UL )

      return 0;



    if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )

      return tt_cmap4_char_map_linear( cmap, &char_code, 0 );

    else

      return tt_cmap4_char_map_binary( cmap, &char_code, 0 );

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap4_char_next( TT_CMap     cmap,

                      FT_UInt32  *pchar_code )

  {

    FT_UInt  gindex;





    if ( *pchar_code >= 0xFFFFU )

      return 0;



    if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )

      gindex = tt_cmap4_char_map_linear( cmap, pchar_code, 1 );

    else

    {

      TT_CMap4  cmap4 = (TT_CMap4)cmap;





      /* no need to search */

      if ( *pchar_code == cmap4->cur_charcode )

      {

        tt_cmap4_next( cmap4 );

        gindex = cmap4->cur_gindex;

        if ( gindex )

          *pchar_code = cmap4->cur_charcode;

      }

      else

        gindex = tt_cmap4_char_map_binary( cmap, pchar_code, 1 );

    }



    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap4_get_info( TT_CMap       cmap,

                     TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 4;





    cmap_info->format   = 4;

    cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap4_class_rec,

      sizeof ( TT_CMap4Rec ),

      (FT_CMap_InitFunc)     tt_cmap4_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap4_char_index,

      (FT_CMap_CharNextFunc) tt_cmap4_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    4,

    (TT_CMap_ValidateFunc)   tt_cmap4_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap4_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_4 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                          FORMAT 6                             *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME        OFFSET          TYPE             DESCRIPTION            */

  /*                                                                       */

  /*   format       0              USHORT           must be 4              */

  /*   length       2              USHORT           table length in bytes  */

  /*   language     4              USHORT           Mac language code      */

  /*                                                                       */

  /*   first        6              USHORT           first segment code     */

  /*   count        8              USHORT           segment size in chars  */

  /*   glyphIds     10             USHORT[count]    glyph IDs              */

  /*                                                                       */

  /* A very simplified segment mapping.                                    */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_6



  FT_CALLBACK_DEF( FT_Error )

  tt_cmap6_validate( FT_Byte*      table,

                     FT_Validator  valid )

  {

    FT_Byte*  p;

    FT_UInt   length, count;





    if ( table + 10 > valid->limit )

      FT_INVALID_TOO_SHORT;



    p      = table + 2;

    length = TT_NEXT_USHORT( p );



    p      = table + 8;             /* skip language and start index */

    count  = TT_NEXT_USHORT( p );



    if ( table + length > valid->limit || length < 10 + count * 2 )

      FT_INVALID_TOO_SHORT;



    /* check glyph indices */

    if ( valid->level >= FT_VALIDATE_TIGHT )

    {

      FT_UInt  gindex;





      for ( ; count > 0; count-- )

      {

        gindex = TT_NEXT_USHORT( p );

        if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) )

          FT_INVALID_GLYPH_ID;

      }

    }



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap6_char_index( TT_CMap    cmap,

                       FT_UInt32  char_code )

  {

    FT_Byte*  table  = cmap->data;

    FT_UInt   result = 0;

    FT_Byte*  p      = table + 6;

    FT_UInt   start  = TT_NEXT_USHORT( p );

    FT_UInt   count  = TT_NEXT_USHORT( p );

    FT_UInt   idx    = (FT_UInt)( char_code - start );





    if ( idx < count )

    {

      p += 2 * idx;

      result = TT_PEEK_USHORT( p );

    }

    return result;

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap6_char_next( TT_CMap     cmap,

                      FT_UInt32  *pchar_code )

  {

    FT_Byte*   table     = cmap->data;

    FT_UInt32  result    = 0;

    FT_UInt32  char_code = *pchar_code + 1;

    FT_UInt    gindex    = 0;



    FT_Byte*   p         = table + 6;

    FT_UInt    start     = TT_NEXT_USHORT( p );

    FT_UInt    count     = TT_NEXT_USHORT( p );

    FT_UInt    idx;





    if ( char_code >= 0x10000UL )

      goto Exit;



    if ( char_code < start )

      char_code = start;



    idx = (FT_UInt)( char_code - start );

    p  += 2 * idx;



    for ( ; idx < count; idx++ )

    {

      gindex = TT_NEXT_USHORT( p );

      if ( gindex != 0 )

      {

        result = char_code;

        break;

      }

      char_code++;

    }



  Exit:

    *pchar_code = result;

    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap6_get_info( TT_CMap       cmap,

                     TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 4;





    cmap_info->format   = 6;

    cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap6_class_rec,

      sizeof ( TT_CMapRec ),



      (FT_CMap_InitFunc)     tt_cmap_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap6_char_index,

      (FT_CMap_CharNextFunc) tt_cmap6_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    6,

    (TT_CMap_ValidateFunc)   tt_cmap6_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap6_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_6 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                          FORMAT 8                             *****/

  /*****                                                               *****/

  /***** It is hard to completely understand what the OpenType spec    *****/

  /***** says about this format, but here is my conclusion.            *****/

  /*****                                                               *****/

  /***** The purpose of this format is to easily map UTF-16 text to    *****/

  /***** glyph indices.  Basically, the `char_code' must be in one of  *****/

  /***** the following formats:                                        *****/

  /*****                                                               *****/

  /*****   - A 16-bit value that isn't part of the Unicode Surrogates  *****/

  /*****     Area (i.e. U+D800-U+DFFF).                                *****/

  /*****                                                               *****/

  /*****   - A 32-bit value, made of two surrogate values, i.e.. if    *****/

  /*****     `char_code = (char_hi << 16) | char_lo', then both        *****/

  /*****     `char_hi' and `char_lo' must be in the Surrogates Area.   *****/

  /*****      Area.                                                    *****/

  /*****                                                               *****/

  /***** The `is32' table embedded in the charmap indicates whether a  *****/

  /***** given 16-bit value is in the surrogates area or not.          *****/

  /*****                                                               *****/

  /***** So, for any given `char_code', we can assert the following:   *****/

  /*****                                                               *****/

  /*****   If `char_hi == 0' then we must have `is32[char_lo] == 0'.   *****/

  /*****                                                               *****/

  /*****   If `char_hi != 0' then we must have both                    *****/

  /*****   `is32[char_hi] != 0' and `is32[char_lo] != 0'.              *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME        OFFSET         TYPE        DESCRIPTION                  */

  /*                                                                       */

  /*   format      0              USHORT      must be 8                    */

  /*   reserved    2              USHORT      reserved                     */

  /*   length      4              ULONG       length in bytes              */

  /*   language    8              ULONG       Mac language code            */

  /*   is32        12             BYTE[8192]  32-bitness bitmap            */

  /*   count       8204           ULONG       number of groups             */

  /*                                                                       */

  /* This header is followed by `count' groups of the following format:    */

  /*                                                                       */

  /*   start       0              ULONG       first charcode               */

  /*   end         4              ULONG       last charcode                */

  /*   startId     8              ULONG       start glyph ID for the group */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_8



  FT_CALLBACK_DEF( FT_Error )

  tt_cmap8_validate( FT_Byte*      table,

                     FT_Validator  valid )

  {

    FT_Byte*   p = table + 4;

    FT_Byte*   is32;

    FT_UInt32  length;

    FT_UInt32  num_groups;





    if ( table + 16 + 8192 > valid->limit )

      FT_INVALID_TOO_SHORT;



    length = TT_NEXT_ULONG( p );

    if ( length > (FT_UInt32)( valid->limit - table ) || length < 8192 + 16 )

      FT_INVALID_TOO_SHORT;



    is32       = table + 12;

    p          = is32  + 8192;          /* skip `is32' array */

    num_groups = TT_NEXT_ULONG( p );



    if ( p + num_groups * 12 > valid->limit )

      FT_INVALID_TOO_SHORT;



    /* check groups, they must be in increasing order */

    {

      FT_UInt32  n, start, end, start_id, count, last = 0;





      for ( n = 0; n < num_groups; n++ )

      {

        FT_UInt   hi, lo;





        start    = TT_NEXT_ULONG( p );

        end      = TT_NEXT_ULONG( p );

        start_id = TT_NEXT_ULONG( p );



        if ( start > end )

          FT_INVALID_DATA;



        if ( n > 0 && start <= last )

          FT_INVALID_DATA;



        if ( valid->level >= FT_VALIDATE_TIGHT )

        {

          if ( start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) )

            FT_INVALID_GLYPH_ID;



          count = (FT_UInt32)( end - start + 1 );



          if ( start & ~0xFFFFU )

          {

            /* start_hi != 0; check that is32[i] is 1 for each i in */

            /* the `hi' and `lo' of the range [start..end]          */

            for ( ; count > 0; count--, start++ )

            {

              hi = (FT_UInt)( start >> 16 );

              lo = (FT_UInt)( start & 0xFFFFU );



              if ( (is32[hi >> 3] & ( 0x80 >> ( hi & 7 ) ) ) == 0 )

                FT_INVALID_DATA;



              if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) == 0 )

                FT_INVALID_DATA;

            }

          }

          else

          {

            /* start_hi == 0; check that is32[i] is 0 for each i in */

            /* the range [start..end]                               */



            /* end_hi cannot be != 0! */

            if ( end & ~0xFFFFU )

              FT_INVALID_DATA;



            for ( ; count > 0; count--, start++ )

            {

              lo = (FT_UInt)( start & 0xFFFFU );



              if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) != 0 )

                FT_INVALID_DATA;

            }

          }

        }



        last = end;

      }

    }



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap8_char_index( TT_CMap    cmap,

                       FT_UInt32  char_code )

  {

    FT_Byte*   table      = cmap->data;

    FT_UInt    result     = 0;

    FT_Byte*   p          = table + 8204;

    FT_UInt32  num_groups = TT_NEXT_ULONG( p );

    FT_UInt32  start, end, start_id;





    for ( ; num_groups > 0; num_groups-- )

    {

      start    = TT_NEXT_ULONG( p );

      end      = TT_NEXT_ULONG( p );

      start_id = TT_NEXT_ULONG( p );



      if ( char_code < start )

        break;



      if ( char_code <= end )

      {

        result = (FT_UInt)( start_id + char_code - start );

        break;

      }

    }

    return result;

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap8_char_next( TT_CMap     cmap,

                      FT_UInt32  *pchar_code )

  {

    FT_UInt32  result     = 0;

    FT_UInt32  char_code  = *pchar_code + 1;

    FT_UInt    gindex     = 0;

    FT_Byte*   table      = cmap->data;

    FT_Byte*   p          = table + 8204;

    FT_UInt32  num_groups = TT_NEXT_ULONG( p );

    FT_UInt32  start, end, start_id;





    p = table + 8208;



    for ( ; num_groups > 0; num_groups-- )

    {

      start    = TT_NEXT_ULONG( p );

      end      = TT_NEXT_ULONG( p );

      start_id = TT_NEXT_ULONG( p );



      if ( char_code < start )

        char_code = start;



      if ( char_code <= end )

      {

        gindex = (FT_UInt)( char_code - start + start_id );

        if ( gindex != 0 )

        {

          result = char_code;

          goto Exit;

        }

      }

    }



  Exit:

    *pchar_code = result;

    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap8_get_info( TT_CMap       cmap,

                     TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 8;





    cmap_info->format   = 8;

    cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap8_class_rec,

      sizeof ( TT_CMapRec ),



      (FT_CMap_InitFunc)     tt_cmap_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap8_char_index,

      (FT_CMap_CharNextFunc) tt_cmap8_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    8,

    (TT_CMap_ValidateFunc)   tt_cmap8_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap8_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_8 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                          FORMAT 10                            *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME      OFFSET  TYPE               DESCRIPTION                    */

  /*                                                                       */

  /*   format     0      USHORT             must be 10                     */

  /*   reserved   2      USHORT             reserved                       */

  /*   length     4      ULONG              length in bytes                */

  /*   language   8      ULONG              Mac language code              */

  /*                                                                       */

  /*   start     12      ULONG              first char in range            */

  /*   count     16      ULONG              number of chars in range       */

  /*   glyphIds  20      USHORT[count]      glyph indices covered          */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_10



  FT_CALLBACK_DEF( FT_Error )

  tt_cmap10_validate( FT_Byte*      table,

                      FT_Validator  valid )

  {

    FT_Byte*  p = table + 4;

    FT_ULong  length, count;





    if ( table + 20 > valid->limit )

      FT_INVALID_TOO_SHORT;



    length = TT_NEXT_ULONG( p );

    p      = table + 16;

    count  = TT_NEXT_ULONG( p );



    if ( length > (FT_ULong)( valid->limit - table ) ||

         length < 20 + count * 2                     )

      FT_INVALID_TOO_SHORT;



    /* check glyph indices */

    if ( valid->level >= FT_VALIDATE_TIGHT )

    {

      FT_UInt  gindex;





      for ( ; count > 0; count-- )

      {

        gindex = TT_NEXT_USHORT( p );

        if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) )

          FT_INVALID_GLYPH_ID;

      }

    }



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap10_char_index( TT_CMap    cmap,

                        FT_UInt32  char_code )

  {

    FT_Byte*   table  = cmap->data;

    FT_UInt    result = 0;

    FT_Byte*   p      = table + 12;

    FT_UInt32  start  = TT_NEXT_ULONG( p );

    FT_UInt32  count  = TT_NEXT_ULONG( p );

    FT_UInt32  idx    = (FT_ULong)( char_code - start );





    if ( idx < count )

    {

      p     += 2 * idx;

      result = TT_PEEK_USHORT( p );

    }

    return result;

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap10_char_next( TT_CMap     cmap,

                       FT_UInt32  *pchar_code )

  {

    FT_Byte*   table     = cmap->data;

    FT_UInt32  char_code = *pchar_code + 1;

    FT_UInt    gindex    = 0;

    FT_Byte*   p         = table + 12;

    FT_UInt32  start     = TT_NEXT_ULONG( p );

    FT_UInt32  count     = TT_NEXT_ULONG( p );

    FT_UInt32  idx;





    if ( char_code < start )

      char_code = start;



    idx = (FT_UInt32)( char_code - start );

    p  += 2 * idx;



    for ( ; idx < count; idx++ )

    {

      gindex = TT_NEXT_USHORT( p );

      if ( gindex != 0 )

        break;

      char_code++;

    }



    *pchar_code = char_code;

    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap10_get_info( TT_CMap       cmap,

                      TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 8;





    cmap_info->format   = 10;

    cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap10_class_rec,

      sizeof ( TT_CMapRec ),



      (FT_CMap_InitFunc)     tt_cmap_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap10_char_index,

      (FT_CMap_CharNextFunc) tt_cmap10_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    10,

    (TT_CMap_ValidateFunc)   tt_cmap10_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap10_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_10 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                          FORMAT 12                            *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME        OFFSET     TYPE       DESCRIPTION                       */

  /*                                                                       */

  /*   format      0          USHORT     must be 12                        */

  /*   reserved    2          USHORT     reserved                          */

  /*   length      4          ULONG      length in bytes                   */

  /*   language    8          ULONG      Mac language code                 */

  /*   count       12         ULONG      number of groups                  */

  /*               16                                                      */

  /*                                                                       */

  /* This header is followed by `count' groups of the following format:    */

  /*                                                                       */

  /*   start       0          ULONG      first charcode                    */

  /*   end         4          ULONG      last charcode                     */

  /*   startId     8          ULONG      start glyph ID for the group      */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_12



  typedef struct  TT_CMap12Rec_

  {

    TT_CMapRec  cmap;

    FT_Bool     valid;

    FT_ULong    cur_charcode;

    FT_UInt     cur_gindex;

    FT_ULong    cur_group;

    FT_ULong    num_groups;



  } TT_CMap12Rec, *TT_CMap12;





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap12_init( TT_CMap12  cmap,

                  FT_Byte*   table )

  {

    cmap->cmap.data  = table;



    table           += 12;

    cmap->num_groups = FT_PEEK_ULONG( table );



    cmap->valid      = 0;



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap12_validate( FT_Byte*      table,

                      FT_Validator  valid )

  {

    FT_Byte*   p;

    FT_ULong   length;

    FT_ULong   num_groups;





    if ( table + 16 > valid->limit )

      FT_INVALID_TOO_SHORT;



    p      = table + 4;

    length = TT_NEXT_ULONG( p );



    p          = table + 12;

    num_groups = TT_NEXT_ULONG( p );



    if ( length > (FT_ULong)( valid->limit - table ) ||

         length < 16 + 12 * num_groups               )

      FT_INVALID_TOO_SHORT;



    /* check groups, they must be in increasing order */

    {

      FT_ULong  n, start, end, start_id, last = 0;





      for ( n = 0; n < num_groups; n++ )

      {

        start    = TT_NEXT_ULONG( p );

        end      = TT_NEXT_ULONG( p );

        start_id = TT_NEXT_ULONG( p );



        if ( start > end )

          FT_INVALID_DATA;



        if ( n > 0 && start <= last )

          FT_INVALID_DATA;



        if ( valid->level >= FT_VALIDATE_TIGHT )

        {

          if ( start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) )

            FT_INVALID_GLYPH_ID;

        }



        last = end;

      }

    }



    return SFNT_Err_Ok;

  }





  /* search the index of the charcode next to cmap->cur_charcode */

  /* cmap->cur_group should be set up properly by caller         */

  /*                                                             */

  static void

  tt_cmap12_next( TT_CMap12  cmap )

  {

    FT_Byte*  p;

    FT_ULong  start, end, start_id, char_code;

    FT_ULong  n;

    FT_UInt   gindex;





    if ( cmap->cur_charcode >= 0xFFFFFFFFUL )

      goto Fail;



    char_code = cmap->cur_charcode + 1;



    n = cmap->cur_group;



    for ( n = cmap->cur_group; n < cmap->num_groups; n++ )

    {

      p        = cmap->cmap.data + 16 + 12 * n;

      start    = TT_NEXT_ULONG( p );

      end      = TT_NEXT_ULONG( p );

      start_id = TT_PEEK_ULONG( p );



      if ( char_code < start )

        char_code = start;



      for ( ; char_code <= end; char_code++ )

      {

        gindex = (FT_UInt)( start_id + char_code - start );



        if ( gindex )

        {

          cmap->cur_charcode = char_code;;

          cmap->cur_gindex   = gindex;

          cmap->cur_group    = n;



          return;

        }

      }

    }



  Fail:

    cmap->valid = 0;

  }





  static FT_UInt

  tt_cmap12_char_map_binary( TT_CMap     cmap,

                             FT_UInt32*  pchar_code,

                             FT_Bool     next )

  {

    FT_UInt    gindex     = 0;

    FT_Byte*   p          = cmap->data + 12;

    FT_UInt32  num_groups = TT_PEEK_ULONG( p );

    FT_UInt32  char_code  = *pchar_code;

    FT_UInt32  start, end, start_id;

    FT_UInt32  max, min, mid;





    if ( !num_groups )

      return 0;



    /* make compiler happy */

    mid = num_groups;

    end = 0xFFFFFFFFUL;



    if ( next )

      char_code++;



    min = 0;

    max = num_groups;



    /* binary search */

    while ( min < max )

    {

      mid = ( min + max ) >> 1;

      p   = cmap->data + 16 + 12 * mid;



      start = TT_NEXT_ULONG( p );

      end   = TT_NEXT_ULONG( p );



      if ( char_code < start )

        max = mid;

      else if ( char_code > end )

        min = mid + 1;

      else

      {

        start_id = TT_PEEK_ULONG( p );

        gindex = (FT_UInt)( start_id + char_code - start );



        break;

      }

    }



    if ( next )

    {

      TT_CMap12  cmap12 = (TT_CMap12)cmap;





      /* if `char_code' is not in any group, then `mid' is */

      /* the group nearest to `char_code'                  */

      /*                                                   */



      if ( char_code > end )

      {

        mid++;

        if ( mid == num_groups )

          return 0;

      }



      cmap12->valid        = 1;

      cmap12->cur_charcode = char_code;

      cmap12->cur_group    = mid;



      if ( !gindex )

      {

        tt_cmap12_next( cmap12 );



        if ( cmap12->valid )

          gindex = cmap12->cur_gindex;

      }

      else

        cmap12->cur_gindex = gindex;



      if ( gindex )

        *pchar_code = cmap12->cur_charcode;

    }



    return gindex;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap12_char_index( TT_CMap    cmap,

                        FT_UInt32  char_code )

  {

    return tt_cmap12_char_map_binary( cmap, &char_code, 0 );

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap12_char_next( TT_CMap     cmap,

                       FT_UInt32  *pchar_code )

  {

    TT_CMap12  cmap12 = (TT_CMap12)cmap;

    FT_ULong   gindex;





    if ( cmap12->cur_charcode >= 0xFFFFFFFFUL )

      return 0;



    /* no need to search */

    if ( cmap12->valid && cmap12->cur_charcode == *pchar_code )

    {

      tt_cmap12_next( cmap12 );

      if ( cmap12->valid )

      {

        gindex = cmap12->cur_gindex;



        /* XXX: check cur_charcode overflow is expected */

        if ( gindex )

          *pchar_code = (FT_UInt32)cmap12->cur_charcode;

      }

      else

        gindex = 0;

    }

    else

      gindex = tt_cmap12_char_map_binary( cmap, pchar_code, 1 );



    /* XXX: check gindex overflow is expected */

    return (FT_UInt32)gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap12_get_info( TT_CMap       cmap,

                      TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 8;





    cmap_info->format   = 12;

    cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap12_class_rec,

      sizeof ( TT_CMap12Rec ),



      (FT_CMap_InitFunc)     tt_cmap12_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap12_char_index,

      (FT_CMap_CharNextFunc) tt_cmap12_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    12,

    (TT_CMap_ValidateFunc)   tt_cmap12_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap12_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_12 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                          FORMAT 13                            *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME        OFFSET     TYPE       DESCRIPTION                       */

  /*                                                                       */

  /*   format      0          USHORT     must be 13                        */

  /*   reserved    2          USHORT     reserved                          */

  /*   length      4          ULONG      length in bytes                   */

  /*   language    8          ULONG      Mac language code                 */

  /*   count       12         ULONG      number of groups                  */

  /*               16                                                      */

  /*                                                                       */

  /* This header is followed by `count' groups of the following format:    */

  /*                                                                       */

  /*   start       0          ULONG      first charcode                    */

  /*   end         4          ULONG      last charcode                     */

  /*   glyphId     8          ULONG      glyph ID for the whole group      */

  /*                                                                       */



#ifdef TT_CONFIG_CMAP_FORMAT_13



  typedef struct  TT_CMap13Rec_

  {

    TT_CMapRec  cmap;

    FT_Bool     valid;

    FT_ULong    cur_charcode;

    FT_UInt     cur_gindex;

    FT_ULong    cur_group;

    FT_ULong    num_groups;



  } TT_CMap13Rec, *TT_CMap13;





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap13_init( TT_CMap13  cmap,

                  FT_Byte*   table )

  {

    cmap->cmap.data  = table;



    table           += 12;

    cmap->num_groups = FT_PEEK_ULONG( table );



    cmap->valid      = 0;



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap13_validate( FT_Byte*      table,

                      FT_Validator  valid )

  {

    FT_Byte*   p;

    FT_ULong   length;

    FT_ULong   num_groups;





    if ( table + 16 > valid->limit )

      FT_INVALID_TOO_SHORT;



    p      = table + 4;

    length = TT_NEXT_ULONG( p );



    p          = table + 12;

    num_groups = TT_NEXT_ULONG( p );



    if ( length > (FT_ULong)( valid->limit - table ) ||

         length < 16 + 12 * num_groups               )

      FT_INVALID_TOO_SHORT;



    /* check groups, they must be in increasing order */

    {

      FT_ULong  n, start, end, glyph_id, last = 0;





      for ( n = 0; n < num_groups; n++ )

      {

        start    = TT_NEXT_ULONG( p );

        end      = TT_NEXT_ULONG( p );

        glyph_id = TT_NEXT_ULONG( p );



        if ( start > end )

          FT_INVALID_DATA;



        if ( n > 0 && start <= last )

          FT_INVALID_DATA;



        if ( valid->level >= FT_VALIDATE_TIGHT )

        {

          if ( glyph_id >= TT_VALID_GLYPH_COUNT( valid ) )

            FT_INVALID_GLYPH_ID;

        }



        last = end;

      }

    }



    return SFNT_Err_Ok;

  }





  /* search the index of the charcode next to cmap->cur_charcode */

  /* cmap->cur_group should be set up properly by caller         */

  /*                                                             */

  static void

  tt_cmap13_next( TT_CMap13  cmap )

  {

    FT_Byte*  p;

    FT_ULong  start, end, glyph_id, char_code;

    FT_ULong  n;

    FT_UInt   gindex;





    if ( cmap->cur_charcode >= 0xFFFFFFFFUL )

      goto Fail;



    char_code = cmap->cur_charcode + 1;



    n = cmap->cur_group;



    for ( n = cmap->cur_group; n < cmap->num_groups; n++ )

    {

      p        = cmap->cmap.data + 16 + 12 * n;

      start    = TT_NEXT_ULONG( p );

      end      = TT_NEXT_ULONG( p );

      glyph_id = TT_PEEK_ULONG( p );



      if ( char_code < start )

        char_code = start;



      if ( char_code <= end )

      {

        gindex = (FT_UInt)glyph_id;



        if ( gindex )

        {

          cmap->cur_charcode = char_code;;

          cmap->cur_gindex   = gindex;

          cmap->cur_group    = n;



          return;

        }

      }

    }



  Fail:

    cmap->valid = 0;

  }





  static FT_UInt

  tt_cmap13_char_map_binary( TT_CMap     cmap,

                             FT_UInt32*  pchar_code,

                             FT_Bool     next )

  {

    FT_UInt    gindex     = 0;

    FT_Byte*   p          = cmap->data + 12;

    FT_UInt32  num_groups = TT_PEEK_ULONG( p );

    FT_UInt32  char_code  = *pchar_code;

    FT_UInt32  start, end;

    FT_UInt32  max, min, mid;





    if ( !num_groups )

      return 0;



    /* make compiler happy */

    mid = num_groups;

    end = 0xFFFFFFFFUL;



    if ( next )

      char_code++;



    min = 0;

    max = num_groups;



    /* binary search */

    while ( min < max )

    {

      mid = ( min + max ) >> 1;

      p   = cmap->data + 16 + 12 * mid;



      start = TT_NEXT_ULONG( p );

      end   = TT_NEXT_ULONG( p );



      if ( char_code < start )

        max = mid;

      else if ( char_code > end )

        min = mid + 1;

      else

      {

        gindex = (FT_UInt)TT_PEEK_ULONG( p );



        break;

      }

    }



    if ( next )

    {

      TT_CMap13  cmap13 = (TT_CMap13)cmap;





      /* if `char_code' is not in any group, then `mid' is */

      /* the group nearest to `char_code'                  */

      /*                                                   */



      if ( char_code > end )

      {

        mid++;

        if ( mid == num_groups )

          return 0;

      }



      cmap13->valid        = 1;

      cmap13->cur_charcode = char_code;

      cmap13->cur_group    = mid;



      if ( !gindex )

      {

        tt_cmap13_next( cmap13 );



        if ( cmap13->valid )

          gindex = cmap13->cur_gindex;

      }

      else

        cmap13->cur_gindex = gindex;



      if ( gindex )

        *pchar_code = cmap13->cur_charcode;

    }



    return gindex;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap13_char_index( TT_CMap    cmap,

                        FT_UInt32  char_code )

  {

    return tt_cmap13_char_map_binary( cmap, &char_code, 0 );

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap13_char_next( TT_CMap     cmap,

                       FT_UInt32  *pchar_code )

  {

    TT_CMap13  cmap13 = (TT_CMap13)cmap;

    FT_UInt    gindex;





    if ( cmap13->cur_charcode >= 0xFFFFFFFFUL )

      return 0;



    /* no need to search */

    if ( cmap13->valid && cmap13->cur_charcode == *pchar_code )

    {

      tt_cmap13_next( cmap13 );

      if ( cmap13->valid )

      {

        gindex = cmap13->cur_gindex;

        if ( gindex )

          *pchar_code = cmap13->cur_charcode;

      }

      else

        gindex = 0;

    }

    else

      gindex = tt_cmap13_char_map_binary( cmap, pchar_code, 1 );



    return gindex;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap13_get_info( TT_CMap       cmap,

                      TT_CMapInfo  *cmap_info )

  {

    FT_Byte*  p = cmap->data + 8;





    cmap_info->format   = 13;

    cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );



    return SFNT_Err_Ok;

  }





  FT_DEFINE_TT_CMAP(tt_cmap13_class_rec,

      sizeof ( TT_CMap13Rec ),



      (FT_CMap_InitFunc)     tt_cmap13_init,

      (FT_CMap_DoneFunc)     NULL,

      (FT_CMap_CharIndexFunc)tt_cmap13_char_index,

      (FT_CMap_CharNextFunc) tt_cmap13_char_next,



      NULL, NULL, NULL, NULL, NULL

    ,

    13,

    (TT_CMap_ValidateFunc)   tt_cmap13_validate,

    (TT_CMap_Info_GetFunc)   tt_cmap13_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_13 */





  /*************************************************************************/

  /*************************************************************************/

  /*****                                                               *****/

  /*****                           FORMAT 14                           *****/

  /*****                                                               *****/

  /*************************************************************************/

  /*************************************************************************/



  /*************************************************************************/

  /*                                                                       */

  /* TABLE OVERVIEW                                                        */

  /* --------------                                                        */

  /*                                                                       */

  /*   NAME         OFFSET  TYPE    DESCRIPTION                            */

  /*                                                                       */

  /*   format         0     USHORT  must be 14                             */

  /*   length         2     ULONG   table length in bytes                  */

  /*   numSelector    6     ULONG   number of variation sel. records       */

  /*                                                                       */

  /* Followed by numSelector records, each of which looks like             */

  /*                                                                       */

  /*   varSelector    0     UINT24  Unicode codepoint of sel.              */

  /*   defaultOff     3     ULONG   offset to a default UVS table          */

  /*                                describing any variants to be found in */

  /*                                the normal Unicode subtable.           */

  /*   nonDefOff      7     ULONG   offset to a non-default UVS table      */

  /*                                describing any variants not in the     */

  /*                                standard cmap, with GIDs here          */

  /* (either offset may be 0 NULL)                                         */

  /*                                                                       */

  /* Selectors are sorted by code point.                                   */

  /*                                                                       */

  /* A default Unicode Variation Selector (UVS) subtable is just a list of */

  /* ranges of code points which are to be found in the standard cmap.  No */

  /* glyph IDs (GIDs) here.                                                */

  /*                                                                       */

  /*   numRanges      0     ULONG   number of ranges following             */

  /*                                                                       */

  /* A range looks like                                                    */

  /*                                                                       */

  /*   uniStart       0     UINT24  code point of the first character in   */

  /*                                this range                             */

  /*   additionalCnt  3     UBYTE   count of additional characters in this */

  /*                                range (zero means a range of a single  */

  /*                                character)                             */

  /*                                                                       */

  /* Ranges are sorted by `uniStart'.                                      */

  /*                                                                       */

  /* A non-default Unicode Variation Selector (UVS) subtable is a list of  */

  /* mappings from codepoint to GID.                                       */

  /*                                                                       */

  /*   numMappings    0     ULONG   number of mappings                     */

  /*                                                                       */

  /* A range looks like                                                    */

  /*                                                                       */

  /*   uniStart       0     UINT24  code point of the first character in   */

  /*                                this range                             */

  /*   GID            3     USHORT  and its GID                            */

  /*                                                                       */

  /* Ranges are sorted by `uniStart'.                                      */



#ifdef TT_CONFIG_CMAP_FORMAT_14



  typedef struct  TT_CMap14Rec_

  {

    TT_CMapRec  cmap;

    FT_ULong    num_selectors;



    /* This array is used to store the results of various

     * cmap 14 query functions.  The data is overwritten

     * on each call to these functions.

     */

    FT_UInt32   max_results;

    FT_UInt32*  results;

    FT_Memory   memory;



  } TT_CMap14Rec, *TT_CMap14;





  FT_CALLBACK_DEF( void )

  tt_cmap14_done( TT_CMap14  cmap )

  {

    FT_Memory  memory = cmap->memory;





    cmap->max_results = 0;

    if ( memory != NULL && cmap->results != NULL )

      FT_FREE( cmap->results );

  }





  static FT_Error

  tt_cmap14_ensure( TT_CMap14  cmap,

                    FT_UInt32  num_results,

                    FT_Memory  memory )

  {

    FT_UInt32 old_max = cmap->max_results;

    FT_Error  error   = SFNT_Err_Ok;





    if ( num_results > cmap->max_results )

    {

       cmap->memory = memory;



       if ( FT_QRENEW_ARRAY( cmap->results, old_max, num_results ) )

         return error;



       cmap->max_results = num_results;

    }



    return error;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap14_init( TT_CMap14  cmap,

                  FT_Byte*   table )

  {

    cmap->cmap.data = table;



    table               += 6;

    cmap->num_selectors = FT_PEEK_ULONG( table );

    cmap->max_results   = 0;

    cmap->results       = NULL;



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap14_validate( FT_Byte*      table,

                      FT_Validator  valid )

  {

    FT_Byte*  p             = table + 2;

    FT_ULong  length        = TT_NEXT_ULONG( p );

    FT_ULong  num_selectors = TT_NEXT_ULONG( p );





    if ( length > (FT_ULong)( valid->limit - table ) ||

         length < 10 + 11 * num_selectors            )

      FT_INVALID_TOO_SHORT;



    /* check selectors, they must be in increasing order */

    {

      /* we start lastVarSel at 1 because a variant selector value of 0

       * isn't valid.

       */

      FT_ULong  n, lastVarSel = 1;





      for ( n = 0; n < num_selectors; n++ )

      {

        FT_ULong  varSel    = TT_NEXT_UINT24( p );

        FT_ULong  defOff    = TT_NEXT_ULONG( p );

        FT_ULong  nondefOff = TT_NEXT_ULONG( p );





        if ( defOff >= length || nondefOff >= length )

          FT_INVALID_TOO_SHORT;



        if ( varSel < lastVarSel )

          FT_INVALID_DATA;



        lastVarSel = varSel + 1;



        /* check the default table (these glyphs should be reached     */

        /* through the normal Unicode cmap, no GIDs, just check order) */

        if ( defOff != 0 )

        {

          FT_Byte*  defp      = table + defOff;

          FT_ULong  numRanges = TT_NEXT_ULONG( defp );

          FT_ULong  i;

          FT_ULong  lastBase  = 0;





          if ( defp + numRanges * 4 > valid->limit )

            FT_INVALID_TOO_SHORT;



          for ( i = 0; i < numRanges; ++i )

          {

            FT_ULong  base = TT_NEXT_UINT24( defp );

            FT_ULong  cnt  = FT_NEXT_BYTE( defp );





            if ( base + cnt >= 0x110000UL )              /* end of Unicode */

              FT_INVALID_DATA;



            if ( base < lastBase )

              FT_INVALID_DATA;



            lastBase = base + cnt + 1U;

          }

        }



        /* and the non-default table (these glyphs are specified here) */

        if ( nondefOff != 0 )

        {

          FT_Byte*  ndp         = table + nondefOff;

          FT_ULong  numMappings = TT_NEXT_ULONG( ndp );

          FT_ULong  i, lastUni = 0;





          if ( numMappings * 4 > (FT_ULong)( valid->limit - ndp ) )

            FT_INVALID_TOO_SHORT;



          for ( i = 0; i < numMappings; ++i )

          {

            FT_ULong  uni = TT_NEXT_UINT24( ndp );

            FT_ULong  gid = TT_NEXT_USHORT( ndp );





            if ( uni >= 0x110000UL )                     /* end of Unicode */

              FT_INVALID_DATA;



            if ( uni < lastUni )

              FT_INVALID_DATA;



            lastUni = uni + 1U;



            if ( valid->level >= FT_VALIDATE_TIGHT    &&

                 gid >= TT_VALID_GLYPH_COUNT( valid ) )

              FT_INVALID_GLYPH_ID;

          }

        }

      }

    }



    return SFNT_Err_Ok;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap14_char_index( TT_CMap    cmap,

                        FT_UInt32  char_code )

  {

    FT_UNUSED( cmap );

    FT_UNUSED( char_code );



    /* This can't happen */

    return 0;

  }





  FT_CALLBACK_DEF( FT_UInt32 )

  tt_cmap14_char_next( TT_CMap     cmap,

                       FT_UInt32  *pchar_code )

  {

    FT_UNUSED( cmap );



    /* This can't happen */

    *pchar_code = 0;

    return 0;

  }





  FT_CALLBACK_DEF( FT_Error )

  tt_cmap14_get_info( TT_CMap       cmap,

                      TT_CMapInfo  *cmap_info )

  {

    FT_UNUSED( cmap );



    cmap_info->format   = 14;

    /* subtable 14 does not define a language field */

    cmap_info->language = 0xFFFFFFFFUL;



    return SFNT_Err_Ok;

  }





  static FT_UInt

  tt_cmap14_char_map_def_binary( FT_Byte    *base,

                                 FT_UInt32   char_code )

  {

    FT_UInt32  numRanges = TT_PEEK_ULONG( base );

    FT_UInt32  max, min;





    min = 0;

    max = numRanges;



    base += 4;



    /* binary search */

    while ( min < max )

    {

      FT_UInt32  mid   = ( min + max ) >> 1;

      FT_Byte*   p     = base + 4 * mid;

      FT_ULong   start = TT_NEXT_UINT24( p );

      FT_UInt    cnt   = FT_NEXT_BYTE( p );





      if ( char_code < start )

        max = mid;

      else if ( char_code > start+cnt )

        min = mid + 1;

      else

        return TRUE;

    }



    return FALSE;

  }





  static FT_UInt

  tt_cmap14_char_map_nondef_binary( FT_Byte    *base,

                                    FT_UInt32   char_code )

  {

    FT_UInt32  numMappings = TT_PEEK_ULONG( base );

    FT_UInt32  max, min;





    min = 0;

    max = numMappings;



    base += 4;



    /* binary search */

    while ( min < max )

    {

      FT_UInt32  mid = ( min + max ) >> 1;

      FT_Byte*   p   = base + 5 * mid;

      FT_UInt32  uni = (FT_UInt32)TT_NEXT_UINT24( p );





      if ( char_code < uni )

        max = mid;

      else if ( char_code > uni )

        min = mid + 1;

      else

        return TT_PEEK_USHORT( p );

    }



    return 0;

  }





  static FT_Byte*

  tt_cmap14_find_variant( FT_Byte    *base,

                          FT_UInt32   variantCode )

  {

    FT_UInt32  numVar = TT_PEEK_ULONG( base );

    FT_UInt32  max, min;





    min = 0;

    max = numVar;



    base += 4;



    /* binary search */

    while ( min < max )

    {

      FT_UInt32  mid    = ( min + max ) >> 1;

      FT_Byte*   p      = base + 11 * mid;

      FT_ULong   varSel = TT_NEXT_UINT24( p );





      if ( variantCode < varSel )

        max = mid;

      else if ( variantCode > varSel )

        min = mid + 1;

      else

        return p;

    }



    return NULL;

  }





  FT_CALLBACK_DEF( FT_UInt )

  tt_cmap14_char_var_index( TT_CMap    cmap,

                            TT_CMap    ucmap,

                            FT_UInt32  charcode,

                            FT_UInt32  variantSelector)

  {

    FT_Byte*  p = tt_cmap14_find_variant( cmap->data + 6, variantSelector );

    FT_ULong  defOff;

    FT_ULong  nondefOff;





    if ( !p )

      return 0;



    defOff    = TT_NEXT_ULONG( p );

    nondefOff = TT_PEEK_ULONG( p );



    if ( defOff != 0                                                    &&

         tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) )

    {

      /* This is the default variant of this charcode.  GID not stored */

      /* here; stored in the normal Unicode charmap instead.           */

      return ucmap->cmap.clazz->char_index( &ucmap->cmap, charcode );

    }



    if ( nondefOff != 0 )

      return tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,

                                               charcode );



    return 0;

  }





  FT_CALLBACK_DEF( FT_Int )

  tt_cmap14_char_var_isdefault( TT_CMap    cmap,

                                FT_UInt32  charcode,

                                FT_UInt32  variantSelector )

  {

    FT_Byte*  p = tt_cmap14_find_variant( cmap->data + 6, variantSelector );

    FT_ULong  defOff;

    FT_ULong  nondefOff;





    if ( !p )

      return -1;



    defOff    = TT_NEXT_ULONG( p );

    nondefOff = TT_NEXT_ULONG( p );



    if ( defOff != 0                                                    &&

         tt_cmap14_char_map_def_binary( cmap->data + defOff, charcode ) )

      return 1;



    if ( nondefOff != 0                                            &&

         tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,

                                           charcode ) != 0         )

      return 0;



    return -1;

  }





  FT_CALLBACK_DEF( FT_UInt32* )

  tt_cmap14_variants( TT_CMap    cmap,

                      FT_Memory  memory )

  {

    TT_CMap14   cmap14 = (TT_CMap14)cmap;

    FT_UInt32   count  = cmap14->num_selectors;

    FT_Byte*    p      = cmap->data + 10;

    FT_UInt32*  result;

    FT_UInt32   i;





    if ( tt_cmap14_ensure( cmap14, ( count + 1 ), memory ) )

      return NULL;



    result = cmap14->results;

    for ( i = 0; i < count; ++i )

    {

      result[i] = (FT_UInt32)TT_NEXT_UINT24( p );

      p        += 8;

    }

    result[i] = 0;



    return result;

  }





  FT_CALLBACK_DEF( FT_UInt32 * )

  tt_cmap14_char_variants( TT_CMap    cmap,

                           FT_Memory  memory,

                           FT_UInt32  charCode )

  {

    TT_CMap14   cmap14 = (TT_CMap14)  cmap;

    FT_UInt32   count  = cmap14->num_selectors;

    FT_Byte*    p      = cmap->data + 10;

    FT_UInt32*  q;





    if ( tt_cmap14_ensure( cmap14, ( count + 1 ), memory ) )

      return NULL;



    for ( q = cmap14->results; count > 0; --count )

    {

      FT_UInt32  varSel    = TT_NEXT_UINT24( p );

      FT_ULong   defOff    = TT_NEXT_ULONG( p );

      FT_ULong   nondefOff = TT_NEXT_ULONG( p );





      if ( ( defOff != 0                                               &&

             tt_cmap14_char_map_def_binary( cmap->data + defOff,

                                            charCode )                 ) ||

           ( nondefOff != 0                                            &&

             tt_cmap14_char_map_nondef_binary( cmap->data + nondefOff,

                                               charCode ) != 0         ) )

      {

        q[0] = varSel;

        q++;

      }

    }

    q[0] = 0;



    return cmap14->results;

  }





  static FT_UInt

  tt_cmap14_def_char_count( FT_Byte  *p )

  {

    FT_UInt32  numRanges = (FT_UInt32)TT_NEXT_ULONG( p );

    FT_UInt    tot       = 0;





    p += 3;  /* point to the first `cnt' field */

    for ( ; numRanges > 0; numRanges-- )

    {

      tot += 1 + p[0];

      p   += 4;

    }



    return tot;

  }





  static FT_UInt32*

  tt_cmap14_get_def_chars( TT_CMap     cmap,

                           FT_Byte*    p,

                           FT_Memory   memory )

  {

    TT_CMap14   cmap14 = (TT_CMap14) cmap;

    FT_UInt32   numRanges;

    FT_UInt     cnt;

    FT_UInt32*  q;





    cnt       = tt_cmap14_def_char_count( p );

    numRanges = (FT_UInt32)TT_NEXT_ULONG( p );



    if ( tt_cmap14_ensure( cmap14, ( cnt + 1 ), memory ) )

      return NULL;



    for ( q = cmap14->results; numRanges > 0; --numRanges )

    {

      FT_UInt32 uni = (FT_UInt32)TT_NEXT_UINT24( p );





      cnt = FT_NEXT_BYTE( p ) + 1;

      do

      {

        q[0]  = uni;

        uni  += 1;

        q    += 1;

      } while ( --cnt != 0 );

    }

    q[0] = 0;



    return cmap14->results;

  }





  static FT_UInt32*

  tt_cmap14_get_nondef_chars( TT_CMap     cmap,

                              FT_Byte    *p,

                              FT_Memory   memory )

  {

    TT_CMap14   cmap14 = (TT_CMap14) cmap;

    FT_UInt32   numMappings;

    FT_UInt     i;

    FT_UInt32  *ret;





    numMappings = (FT_UInt32)TT_NEXT_ULONG( p );



    if ( tt_cmap14_ensure( cmap14, ( numMappings + 1 ), memory ) )

      return NULL;



    ret = cmap14->results;

    for ( i = 0; i < numMappings; ++i )

    {

      ret[i] = (FT_UInt32)TT_NEXT_UINT24( p );

      p += 2;

    }

    ret[i] = 0;



    return ret;

  }





  FT_CALLBACK_DEF( FT_UInt32 * )

  tt_cmap14_variant_chars( TT_CMap    cmap,

                           FT_Memory  memory,

                           FT_UInt32  variantSelector )

  {

    FT_Byte    *p  = tt_cmap14_find_variant( cmap->data + 6,

                                             variantSelector );

    FT_UInt32  *ret;

    FT_Int      i;

    FT_ULong    defOff;

    FT_ULong    nondefOff;





    if ( !p )

      return NULL;



    defOff    = TT_NEXT_ULONG( p );

    nondefOff = TT_NEXT_ULONG( p );



    if ( defOff == 0 && nondefOff == 0 )

      return NULL;



    if ( defOff == 0 )

      return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff,

                                         memory );

    else if ( nondefOff == 0 )

      return tt_cmap14_get_def_chars( cmap, cmap->data + defOff,

                                      memory );

    else

    {

      /* Both a default and a non-default glyph set?  That's probably not */

      /* good font design, but the spec allows for it...                  */

      TT_CMap14  cmap14 = (TT_CMap14) cmap;

      FT_UInt32  numRanges;

      FT_UInt32  numMappings;

      FT_UInt32  duni;

      FT_UInt32  dcnt;

      FT_UInt32  nuni;

      FT_Byte*   dp;

      FT_UInt    di, ni, k;





      p  = cmap->data + nondefOff;

      dp = cmap->data + defOff;



      numMappings = (FT_UInt32)TT_NEXT_ULONG( p );

      dcnt        = tt_cmap14_def_char_count( dp );

      numRanges   = (FT_UInt32)TT_NEXT_ULONG( dp );



      if ( numMappings == 0 )

        return tt_cmap14_get_def_chars( cmap, cmap->data + defOff,

                                        memory );

      if ( dcnt == 0 )

        return tt_cmap14_get_nondef_chars( cmap, cmap->data + nondefOff,

                                           memory );



      if ( tt_cmap14_ensure( cmap14, ( dcnt + numMappings + 1 ), memory ) )

        return NULL;



      ret  = cmap14->results;

      duni = (FT_UInt32)TT_NEXT_UINT24( dp );

      dcnt = FT_NEXT_BYTE( dp );

      di   = 1;

      nuni = (FT_UInt32)TT_NEXT_UINT24( p );

      p   += 2;

      ni   = 1;

      i    = 0;



      for ( ;; )

      {

        if ( nuni > duni + dcnt )

        {

          for ( k = 0; k <= dcnt; ++k )

            ret[i++] = duni + k;



          ++di;



          if ( di > numRanges )

            break;



          duni = (FT_UInt32)TT_NEXT_UINT24( dp );

          dcnt = FT_NEXT_BYTE( dp );

        }

        else

        {

          if ( nuni < duni )

            ret[i++] = nuni;

          /* If it is within the default range then ignore it -- */

          /* that should not have happened                       */

          ++ni;

          if ( ni > numMappings )

            break;



          nuni = (FT_UInt32)TT_NEXT_UINT24( p );

          p += 2;

        }

      }



      if ( ni <= numMappings )

      {

        /* If we get here then we have run out of all default ranges.   */

        /* We have read one non-default mapping which we haven't stored */

        /* and there may be others that need to be read.                */

        ret[i++] = nuni;

        while ( ni < numMappings )

        {

          ret[i++] = (FT_UInt32)TT_NEXT_UINT24( p );

          p += 2;

          ++ni;

        }

      }

      else if ( di <= numRanges )

      {

        /* If we get here then we have run out of all non-default     */

        /* mappings.  We have read one default range which we haven't */

        /* stored and there may be others that need to be read.       */

        for ( k = 0; k <= dcnt; ++k )

          ret[i++] = duni + k;



        while ( di < numRanges )

        {

          duni = (FT_UInt32)TT_NEXT_UINT24( dp );

          dcnt = FT_NEXT_BYTE( dp );



          for ( k = 0; k <= dcnt; ++k )

            ret[i++] = duni + k;

          ++di;

        }

      }



      ret[i] = 0;



      return ret;

    }

  }





  FT_DEFINE_TT_CMAP(tt_cmap14_class_rec,

      sizeof ( TT_CMap14Rec ),



      (FT_CMap_InitFunc)     tt_cmap14_init,

      (FT_CMap_DoneFunc)     tt_cmap14_done,

      (FT_CMap_CharIndexFunc)tt_cmap14_char_index,

      (FT_CMap_CharNextFunc) tt_cmap14_char_next,



      /* Format 14 extension functions */

      (FT_CMap_CharVarIndexFunc)    tt_cmap14_char_var_index,

      (FT_CMap_CharVarIsDefaultFunc)tt_cmap14_char_var_isdefault,

      (FT_CMap_VariantListFunc)     tt_cmap14_variants,

      (FT_CMap_CharVariantListFunc) tt_cmap14_char_variants,

      (FT_CMap_VariantCharListFunc) tt_cmap14_variant_chars

    ,

    14,

    (TT_CMap_ValidateFunc)tt_cmap14_validate,

    (TT_CMap_Info_GetFunc)tt_cmap14_get_info

  )



#endif /* TT_CONFIG_CMAP_FORMAT_14 */





#ifndef FT_CONFIG_OPTION_PIC



  static const TT_CMap_Class  tt_cmap_classes[] =

  {

#define TTCMAPCITEM(a) &a,

#include "ttcmapc.h"

    NULL,

  };



#else /*FT_CONFIG_OPTION_PIC*/



  void FT_Destroy_Class_tt_cmap_classes(FT_Library library, TT_CMap_Class* clazz)

  {

    FT_Memory memory = library->memory;

    if ( clazz )

      FT_FREE( clazz );

  }



  FT_Error FT_Create_Class_tt_cmap_classes(FT_Library library, TT_CMap_Class** output_class)

  {

    TT_CMap_Class*  clazz;

    TT_CMap_ClassRec* recs;

    FT_Error          error;

    FT_Memory memory = library->memory;

    int i = 0;



#define TTCMAPCITEM(a) i++;

#include "ttcmapc.h"



    /* allocate enough space for both the pointers +terminator and the class instances */

    if ( FT_ALLOC( clazz, sizeof(*clazz)*(i+1)+sizeof(TT_CMap_ClassRec)*i ) )

      return error;



    /* the location of the class instances follows the array of pointers */

    recs = (TT_CMap_ClassRec*) (((char*)clazz)+(sizeof(*clazz)*(i+1))); 

    i=0;



#undef TTCMAPCITEM

#define TTCMAPCITEM(a)           \

    FT_Init_Class_##a(&recs[i]); \

    clazz[i] = &recs[i];         \

    i++;

#include "ttcmapc.h"



    clazz[i] = NULL;



    *output_class = clazz;

    return SFNT_Err_Ok;

  }



#endif /*FT_CONFIG_OPTION_PIC*/





  /* parse the `cmap' table and build the corresponding TT_CMap objects */

  /* in the current face                                                */

  /*                                                                    */

  FT_LOCAL_DEF( FT_Error )

  tt_face_build_cmaps( TT_Face  face )

  {

    FT_Byte*           table = face->cmap_table;

    FT_Byte*           limit = table + face->cmap_size;

    FT_UInt volatile   num_cmaps;

    FT_Byte* volatile  p     = table;

    FT_Library         library = FT_FACE_LIBRARY( face );



    FT_UNUSED( library );





    if ( !p || p + 4 > limit )

      return SFNT_Err_Invalid_Table;



    /* only recognize format 0 */

    if ( TT_NEXT_USHORT( p ) != 0 )

    {

      p -= 2;

      FT_ERROR(( "tt_face_build_cmaps:"

                 " unsupported `cmap' table format = %d\n",

                 TT_PEEK_USHORT( p ) ));

      return SFNT_Err_Invalid_Table;

    }



    num_cmaps = TT_NEXT_USHORT( p );

#ifdef FT_MAX_CHARMAP_CACHEABLE

    if ( num_cmaps > FT_MAX_CHARMAP_CACHEABLE )

      FT_ERROR(( "tt_face_build_cmaps: too many cmap subtables(%d) "

                 "subtable#%d and later are loaded but cannot be searched\n",

                 num_cmaps, FT_MAX_CHARMAP_CACHEABLE + 1 ));

#endif



    for ( ; num_cmaps > 0 && p + 8 <= limit; num_cmaps-- )

    {

      FT_CharMapRec  charmap;

      FT_UInt32      offset;





      charmap.platform_id = TT_NEXT_USHORT( p );

      charmap.encoding_id = TT_NEXT_USHORT( p );

      charmap.face        = FT_FACE( face );

      charmap.encoding    = FT_ENCODING_NONE;  /* will be filled later */

      offset              = TT_NEXT_ULONG( p );



      if ( offset && offset <= face->cmap_size - 2 )

      {

        FT_Byte* volatile              cmap   = table + offset;

        volatile FT_UInt               format = TT_PEEK_USHORT( cmap );

        const TT_CMap_Class* volatile  pclazz = FT_TT_CMAP_CLASSES_GET;

        TT_CMap_Class volatile         clazz;





        for ( ; *pclazz; pclazz++ )

        {

          clazz = *pclazz;

          if ( clazz->format == format )

          {

            volatile TT_ValidatorRec  valid;

            volatile FT_Error         error = SFNT_Err_Ok;





            ft_validator_init( FT_VALIDATOR( &valid ), cmap, limit,

                               FT_VALIDATE_DEFAULT );



            valid.num_glyphs = (FT_UInt)face->max_profile.numGlyphs;



            if ( ft_setjmp(

              *((ft_jmp_buf*)&FT_VALIDATOR( &valid )->jump_buffer) ) == 0 )

            {

              /* validate this cmap sub-table */

              error = clazz->validate( cmap, FT_VALIDATOR( &valid ) );

            }



            if ( valid.validator.error == 0 )

            {

              FT_CMap  ttcmap;





              /* It might make sense to store the single variation selector */

              /* cmap somewhere special.  But it would have to be in the    */

              /* public FT_FaceRec, and we can't change that.               */



              if ( !FT_CMap_New( (FT_CMap_Class)clazz,

                                 cmap, &charmap, &ttcmap ) )

              {

                /* it is simpler to directly set `flags' than adding */

                /* a parameter to FT_CMap_New                        */

                ((TT_CMap)ttcmap)->flags = (FT_Int)error;

              }

            }

            else

            {

              FT_TRACE0(( "tt_face_build_cmaps:"

                          " broken cmap sub-table ignored\n" ));

            }

            break;

          }

        }



        if ( *pclazz == NULL )

        {

          FT_TRACE0(( "tt_face_build_cmaps:"

                      " unsupported cmap sub-table ignored\n" ));

        }

      }

    }



    return SFNT_Err_Ok;

  }





  FT_LOCAL( FT_Error )

  tt_get_cmap_info( FT_CharMap    charmap,

                    TT_CMapInfo  *cmap_info )

  {

    FT_CMap        cmap  = (FT_CMap)charmap;

    TT_CMap_Class  clazz = (TT_CMap_Class)cmap->clazz;





    return clazz->get_cmap_info( charmap, cmap_info );

  }





/* END */

