Edit

kc3-lang/freetype/src/sfnt/ttcmap.c

Branch :

  • Show log

    Commit

  • Author : suzuki toshiya
    Date : 2010-07-04 20:14:18
    Hash : 0ae32718
    Message : Restrict the number of the charmaps in a rogue-compatible mode. Fix for Savannah bug #30059. * src/cache/ftccmap.c (FTC_CMapCache_Lookup): Replace `16' the minimum character code passed by a legacy rogue client by... * include/freetype/config/ftoption.h (FT_MAX_CHARMAP_CACHEABLE): This. It is undefined when FT_CONFIG_OPTION_OLD_INTERNALS is undefined (thus the rogue client compatibility is not required). * src/cff/cffobjs.c (cff_face_init): Abort the automatic selection or synthesis of Unicode cmap subtable when the charmap index exceeds FT_MAX_CHARMAP_CACHEABLE. * src/sfnt/ttcmap.c (tt_face_build_cmaps): Issue error message when the charmap index exceeds FT_MAX_CHARMAP_CACHEABLE. * src/base/ftobjs.c (find_unicode_charmap): When Unicode charmap is found after FT_MAX_CHARMAP_CACHEABLE, ignore it and search earlier one. (find_variant_selector_charmap): When UVS charmap is found after FT_MAX_CHARMAP_CACHEABLE, ignore it and search earlier one. (FT_Select_Charmap): When a charmap matching with requested encoding but after FT_MAX_CHARMAP_CACHEABLE, ignore and search earlier one. (FT_Set_Charmap): When a charmap matching with requested charmap but after FT_MAX_CHARMAP_CACHEABLE, ignore and search earlier one. (FT_Get_Charmap_Index): When a requested charmap is found after FT_MAX_CHARMAP_CACHEABLE, return the inverted charmap index.

  • src/sfnt/ttcmap.c
  • /***************************************************************************/
    /*                                                                         */
    /*  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 */