Edit

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

Branch :

  • Show log

    Commit

  • Author : Werner Lemberg
    Date : 2023-02-08 19:36:10
    Hash : ac5babe8
    Message : Fix 'fall-through' warning messages. Modern compilers get more insistent on that... * include/freetype/internal/compiler-macros.h (FALL_THROUGH): Define. * src/*: Use it instead of `/* fall through */` comments.

  • src/sfnt/sfdriver.c
  • /****************************************************************************
     *
     * sfdriver.c
     *
     *   High-level SFNT driver interface (body).
     *
     * Copyright (C) 1996-2023 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 <freetype/internal/ftdebug.h>
    #include <freetype/internal/sfnt.h>
    #include <freetype/internal/ftobjs.h>
    #include <freetype/ttnameid.h>
    
    #include "sfdriver.h"
    #include "ttload.h"
    #include "sfobjs.h"
    
    #include "sferrors.h"
    
    #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    #include "ttsbit.h"
    #endif
    
    #ifdef TT_CONFIG_OPTION_COLOR_LAYERS
    #include "ttcolr.h"
    #include "ttcpal.h"
    #endif
    
    #ifdef FT_CONFIG_OPTION_SVG
    #include "ttsvg.h"
    #endif
    
    #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
    #include "ttpost.h"
    #endif
    
    #ifdef TT_CONFIG_OPTION_BDF
    #include "ttbdf.h"
    #include <freetype/internal/services/svbdf.h>
    #endif
    
    #include "ttcmap.h"
    #include "ttkern.h"
    #include "ttmtx.h"
    
    #include <freetype/internal/services/svgldict.h>
    #include <freetype/internal/services/svpostnm.h>
    #include <freetype/internal/services/svsfnt.h>
    #include <freetype/internal/services/svttcmap.h>
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    #include <freetype/ftmm.h>
    #include <freetype/internal/services/svmm.h>
    #endif
    
    
      /**************************************************************************
       *
       * 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  sfdriver
    
    
      /*
       * SFNT TABLE SERVICE
       *
       */
    
      static void*
      get_sfnt_table( TT_Face      face,
                      FT_Sfnt_Tag  tag )
      {
        void*  table;
    
    
        switch ( tag )
        {
        case FT_SFNT_HEAD:
          table = &face->header;
          break;
    
        case FT_SFNT_HHEA:
          table = &face->horizontal;
          break;
    
        case FT_SFNT_VHEA:
          table = face->vertical_info ? &face->vertical : NULL;
          break;
    
        case FT_SFNT_OS2:
          table = ( face->os2.version == 0xFFFFU ) ? NULL : &face->os2;
          break;
    
        case FT_SFNT_POST:
          table = &face->postscript;
          break;
    
        case FT_SFNT_MAXP:
          table = &face->max_profile;
          break;
    
        case FT_SFNT_PCLT:
          table = face->pclt.Version ? &face->pclt : NULL;
          break;
    
        default:
          table = NULL;
        }
    
        return table;
      }
    
    
      static FT_Error
      sfnt_table_info( TT_Face    face,
                       FT_UInt    idx,
                       FT_ULong  *tag,
                       FT_ULong  *offset,
                       FT_ULong  *length )
      {
        if ( !offset || !length )
          return FT_THROW( Invalid_Argument );
    
        if ( !tag )
          *length = face->num_tables;
        else
        {
          if ( idx >= face->num_tables )
            return FT_THROW( Table_Missing );
    
          *tag    = face->dir_tables[idx].Tag;
          *offset = face->dir_tables[idx].Offset;
          *length = face->dir_tables[idx].Length;
        }
    
        return FT_Err_Ok;
      }
    
    
      FT_DEFINE_SERVICE_SFNT_TABLEREC(
        sfnt_service_sfnt_table,
    
        (FT_SFNT_TableLoadFunc)tt_face_load_any,     /* load_table */
        (FT_SFNT_TableGetFunc) get_sfnt_table,       /* get_table  */
        (FT_SFNT_TableInfoFunc)sfnt_table_info       /* table_info */
      )
    
    
    #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
    
      /*
       * GLYPH DICT SERVICE
       *
       */
    
      static FT_Error
      sfnt_get_glyph_name( FT_Face     face,
                           FT_UInt     glyph_index,
                           FT_Pointer  buffer,
                           FT_UInt     buffer_max )
      {
        FT_String*  gname;
        FT_Error    error;
    
    
        error = tt_face_get_ps_name( (TT_Face)face, glyph_index, &gname );
        if ( !error )
          FT_STRCPYN( buffer, gname, buffer_max );
    
        return error;
      }
    
    
      static FT_UInt
      sfnt_get_name_index( FT_Face           face,
                           const FT_String*  glyph_name )
      {
        TT_Face  ttface = (TT_Face)face;
    
        FT_UInt  i, max_gid = FT_UINT_MAX;
    
    
        if ( face->num_glyphs < 0 )
          return 0;
        else if ( (FT_ULong)face->num_glyphs < FT_UINT_MAX )
          max_gid = (FT_UInt)face->num_glyphs;
        else
          FT_TRACE0(( "Ignore glyph names for invalid GID 0x%08x - 0x%08lx\n",
                      FT_UINT_MAX, face->num_glyphs ));
    
        for ( i = 0; i < max_gid; i++ )
        {
          FT_String*  gname;
          FT_Error    error = tt_face_get_ps_name( ttface, i, &gname );
    
    
          if ( error )
            continue;
    
          if ( !ft_strcmp( glyph_name, gname ) )
            return i;
        }
    
        return 0;
      }
    
    
      FT_DEFINE_SERVICE_GLYPHDICTREC(
        sfnt_service_glyph_dict,
    
        (FT_GlyphDict_GetNameFunc)  sfnt_get_glyph_name,    /* get_name   */
        (FT_GlyphDict_NameIndexFunc)sfnt_get_name_index     /* name_index */
      )
    
    #endif /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
    
    
      /*
       * POSTSCRIPT NAME SERVICE
       *
       */
    
      /* an array representing allowed ASCII characters in a PS string */
      static const unsigned char sfnt_ps_map[16] =
      {
                    /*             4        0        C        8 */
        0x00, 0x00, /* 0x00: 0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 0 */
        0x00, 0x00, /* 0x10: 0 0 0 0  0 0 0 0  0 0 0 0  0 0 0 0 */
        0xDE, 0x7C, /* 0x20: 1 1 0 1  1 1 1 0  0 1 1 1  1 1 0 0 */
        0xFF, 0xAF, /* 0x30: 1 1 1 1  1 1 1 1  1 0 1 0  1 1 1 1 */
        0xFF, 0xFF, /* 0x40: 1 1 1 1  1 1 1 1  1 1 1 1  1 1 1 1 */
        0xFF, 0xD7, /* 0x50: 1 1 1 1  1 1 1 1  1 1 0 1  0 1 1 1 */
        0xFF, 0xFF, /* 0x60: 1 1 1 1  1 1 1 1  1 1 1 1  1 1 1 1 */
        0xFF, 0x57  /* 0x70: 1 1 1 1  1 1 1 1  0 1 0 1  0 1 1 1 */
      };
    
    
      static int
      sfnt_is_postscript( int  c )
      {
        unsigned int  cc;
    
    
        if ( c < 0 || c >= 0x80 )
          return 0;
    
        cc = (unsigned int)c;
    
        return sfnt_ps_map[cc >> 3] & ( 1 << ( cc & 0x07 ) );
      }
    
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    
      /* Only ASCII letters and digits are taken for a variation font */
      /* instance's PostScript name.                                  */
      /*                                                              */
      /* `ft_isalnum' is a macro, but we need a function here, thus   */
      /* this definition.                                             */
      static int
      sfnt_is_alphanumeric( int  c )
      {
        return ft_isalnum( c );
      }
    
    
      /* the implementation of MurmurHash3 is taken and adapted from          */
      /* https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp */
    
    #define ROTL32( x, r )  ( x << r ) | ( x >> ( 32 - r ) )
    
    
      static FT_UInt32
      fmix32( FT_UInt32  h )
      {
        h ^= h >> 16;
        h *= 0x85ebca6b;
        h ^= h >> 13;
        h *= 0xc2b2ae35;
        h ^= h >> 16;
    
        return h;
      }
    
    
      static void
      murmur_hash_3_128( const void*         key,
                         const unsigned int  len,
                         FT_UInt32           seed,
                         void*               out )
      {
        const FT_Byte*  data    = (const FT_Byte*)key;
        const int       nblocks = (int)len / 16;
    
        FT_UInt32  h1 = seed;
        FT_UInt32  h2 = seed;
        FT_UInt32  h3 = seed;
        FT_UInt32  h4 = seed;
    
        const FT_UInt32  c1 = 0x239b961b;
        const FT_UInt32  c2 = 0xab0e9789;
        const FT_UInt32  c3 = 0x38b34ae5;
        const FT_UInt32  c4 = 0xa1e38b93;
    
        const FT_UInt32*  blocks = (const FT_UInt32*)( data + nblocks * 16 );
    
        int  i;
    
    
        for( i = -nblocks; i; i++ )
        {
          FT_UInt32  k1 = blocks[i * 4 + 0];
          FT_UInt32  k2 = blocks[i * 4 + 1];
          FT_UInt32  k3 = blocks[i * 4 + 2];
          FT_UInt32  k4 = blocks[i * 4 + 3];
    
    
          k1 *= c1;
          k1  = ROTL32( k1, 15 );
          k1 *= c2;
          h1 ^= k1;
    
          h1  = ROTL32( h1, 19 );
          h1 += h2;
          h1  = h1 * 5 + 0x561ccd1b;
    
          k2 *= c2;
          k2  = ROTL32( k2, 16 );
          k2 *= c3;
          h2 ^= k2;
    
          h2  = ROTL32( h2, 17 );
          h2 += h3;
          h2  = h2 * 5 + 0x0bcaa747;
    
          k3 *= c3;
          k3  = ROTL32( k3, 17 );
          k3 *= c4;
          h3 ^= k3;
    
          h3  = ROTL32( h3, 15 );
          h3 += h4;
          h3  = h3 * 5 + 0x96cd1c35;
    
          k4 *= c4;
          k4  = ROTL32( k4, 18 );
          k4 *= c1;
          h4 ^= k4;
    
          h4  = ROTL32( h4, 13 );
          h4 += h1;
          h4  = h4 * 5 + 0x32ac3b17;
        }
    
        {
          const FT_Byte*  tail = (const FT_Byte*)( data + nblocks * 16 );
    
          FT_UInt32  k1 = 0;
          FT_UInt32  k2 = 0;
          FT_UInt32  k3 = 0;
          FT_UInt32  k4 = 0;
    
    
          switch ( len & 15 )
          {
          case 15:
            k4 ^= (FT_UInt32)tail[14] << 16;
            FALL_THROUGH;
          case 14:
            k4 ^= (FT_UInt32)tail[13] << 8;
            FALL_THROUGH;
          case 13:
            k4 ^= (FT_UInt32)tail[12];
            k4 *= c4;
            k4  = ROTL32( k4, 18 );
            k4 *= c1;
            h4 ^= k4;
            FALL_THROUGH;
    
          case 12:
            k3 ^= (FT_UInt32)tail[11] << 24;
            FALL_THROUGH;
          case 11:
            k3 ^= (FT_UInt32)tail[10] << 16;
            FALL_THROUGH;
          case 10:
            k3 ^= (FT_UInt32)tail[9] << 8;
            FALL_THROUGH;
          case 9:
            k3 ^= (FT_UInt32)tail[8];
            k3 *= c3;
            k3  = ROTL32( k3, 17 );
            k3 *= c4;
            h3 ^= k3;
            FALL_THROUGH;
    
          case 8:
            k2 ^= (FT_UInt32)tail[7] << 24;
            FALL_THROUGH;
          case 7:
            k2 ^= (FT_UInt32)tail[6] << 16;
            FALL_THROUGH;
          case 6:
            k2 ^= (FT_UInt32)tail[5] << 8;
            FALL_THROUGH;
          case 5:
            k2 ^= (FT_UInt32)tail[4];
            k2 *= c2;
            k2  = ROTL32( k2, 16 );
            k2 *= c3;
            h2 ^= k2;
            FALL_THROUGH;
    
          case 4:
            k1 ^= (FT_UInt32)tail[3] << 24;
            FALL_THROUGH;
          case 3:
            k1 ^= (FT_UInt32)tail[2] << 16;
            FALL_THROUGH;
          case 2:
            k1 ^= (FT_UInt32)tail[1] << 8;
            FALL_THROUGH;
          case 1:
            k1 ^= (FT_UInt32)tail[0];
            k1 *= c1;
            k1  = ROTL32( k1, 15 );
            k1 *= c2;
            h1 ^= k1;
          }
        }
    
        h1 ^= len;
        h2 ^= len;
        h3 ^= len;
        h4 ^= len;
    
        h1 += h2;
        h1 += h3;
        h1 += h4;
    
        h2 += h1;
        h3 += h1;
        h4 += h1;
    
        h1 = fmix32( h1 );
        h2 = fmix32( h2 );
        h3 = fmix32( h3 );
        h4 = fmix32( h4 );
    
        h1 += h2;
        h1 += h3;
        h1 += h4;
    
        h2 += h1;
        h3 += h1;
        h4 += h1;
    
        ((FT_UInt32*)out)[0] = h1;
        ((FT_UInt32*)out)[1] = h2;
        ((FT_UInt32*)out)[2] = h3;
        ((FT_UInt32*)out)[3] = h4;
      }
    
    
    #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
    
    
      typedef int (*char_type_func)( int  c );
    
    
      /* Handling of PID/EID 3/0 and 3/1 is the same. */
    #define IS_WIN( n )  ( (n)->platformID == 3                             && \
                           ( (n)->encodingID == 1 || (n)->encodingID == 0 ) )
    
    #define IS_APPLE( n )  ( (n)->platformID == 1 && \
                             (n)->encodingID == 0 )
    
      static char*
      get_win_string( FT_Memory       memory,
                      FT_Stream       stream,
                      TT_Name         entry,
                      char_type_func  char_type,
                      FT_Bool         report_invalid_characters )
      {
        FT_Error  error;
    
        char*       result = NULL;
        FT_String*  r;
        FT_Char*    p;
        FT_UInt     len;
    
    
        if ( FT_QALLOC( result, entry->stringLength / 2 + 1 ) )
          return NULL;
    
        if ( FT_STREAM_SEEK( entry->stringOffset ) ||
             FT_FRAME_ENTER( entry->stringLength ) )
          goto get_win_string_error;
    
        r = (FT_String*)result;
        p = (FT_Char*)stream->cursor;
    
        for ( len = entry->stringLength / 2; len > 0; len--, p += 2 )
        {
          if ( p[0] == 0 && char_type( p[1] ) )
            *r++ = p[1];
          else
          {
            if ( report_invalid_characters )
              FT_TRACE0(( "get_win_string:"
                          " Character 0x%X invalid in PS name string\n",
                          ((unsigned)p[0])*256 + (unsigned)p[1] ));
            break;
          }
        }
        if ( !len )
          *r = '\0';
    
        FT_FRAME_EXIT();
    
        if ( !len )
          return result;
    
      get_win_string_error:
        FT_FREE( result );
    
        entry->stringLength = 0;
        entry->stringOffset = 0;
        FT_FREE( entry->string );
    
        return NULL;
      }
    
    
      static char*
      get_apple_string( FT_Memory       memory,
                        FT_Stream       stream,
                        TT_Name         entry,
                        char_type_func  char_type,
                        FT_Bool         report_invalid_characters )
      {
        FT_Error  error;
    
        char*       result = NULL;
        FT_String*  r;
        FT_Char*    p;
        FT_UInt     len;
    
    
        if ( FT_QALLOC( result, entry->stringLength + 1 ) )
          return NULL;
    
        if ( FT_STREAM_SEEK( entry->stringOffset ) ||
             FT_FRAME_ENTER( entry->stringLength ) )
          goto get_apple_string_error;
    
        r = (FT_String*)result;
        p = (FT_Char*)stream->cursor;
    
        for ( len = entry->stringLength; len > 0; len--, p++ )
        {
          if ( char_type( *p ) )
            *r++ = *p;
          else
          {
            if ( report_invalid_characters )
              FT_TRACE0(( "get_apple_string:"
                          " Character `%c' (0x%X) invalid in PS name string\n",
                          *p, *p ));
            break;
          }
        }
        if ( !len )
          *r = '\0';
    
        FT_FRAME_EXIT();
    
        if ( !len )
          return result;
    
      get_apple_string_error:
        FT_FREE( result );
    
        entry->stringOffset = 0;
        entry->stringLength = 0;
        FT_FREE( entry->string );
    
        return NULL;
      }
    
    
      static FT_Bool
      sfnt_get_name_id( TT_Face    face,
                        FT_UShort  id,
                        FT_Int    *win,
                        FT_Int    *apple )
      {
        FT_Int  n;
    
    
        *win   = -1;
        *apple = -1;
    
        for ( n = 0; n < face->num_names; n++ )
        {
          TT_Name  name = face->name_table.names + n;
    
    
          if ( name->nameID == id && name->stringLength > 0 )
          {
            if ( IS_WIN( name ) && ( name->languageID == 0x409 || *win == -1 ) )
              *win = n;
    
            if ( IS_APPLE( name ) && ( name->languageID == 0 || *apple == -1 ) )
              *apple = n;
          }
        }
    
        return ( *win >= 0 ) || ( *apple >= 0 );
      }
    
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    
      /*
          The maximum length of an axis value descriptor.
    
          We need 65536 different values for the decimal fraction; this fits
          nicely into five decimal places.  Consequently, it consists of
    
            . the minus sign if the number is negative,
            . up to five characters for the digits before the decimal point,
            . the decimal point if there is a fractional part, and
            . up to five characters for the digits after the decimal point.
    
          We also need one byte for the leading `_' character and up to four
          bytes for the axis tag.
       */
    #define MAX_VALUE_DESCRIPTOR_LEN  ( 1 + 5 + 1 + 5 + 1 + 4 )
    
    
      /* the maximum length of PostScript font names */
    #define MAX_PS_NAME_LEN  127
    
    
      /*
       * Find the shortest decimal representation of a 16.16 fixed-point
       * number.  The function fills `buf' with the result, returning a pointer
       * to the position after the representation's last byte.
       */
    
      static char*
      fixed2float( FT_Int  fixed,
                   char*   buf )
      {
        char*  p;
        char*  q;
        char   tmp[5];
    
        FT_Int  int_part;
        FT_Int  frac_part;
    
        FT_Int  i;
    
    
        p = buf;
    
        if ( fixed == 0 )
        {
          *p++ = '0';
          return p;
        }
    
        if ( fixed < 0 )
        {
          *p++ = '-';
          fixed = NEG_INT( fixed );
        }
    
        int_part  = ( fixed >> 16 ) & 0xFFFF;
        frac_part = fixed & 0xFFFF;
    
        /* get digits of integer part (in reverse order) */
        q = tmp;
        while ( int_part > 0 )
        {
          *q++      = '0' + int_part % 10;
          int_part /= 10;
        }
    
        /* copy digits in correct order to buffer */
        while ( q > tmp )
          *p++ = *--q;
    
        if ( !frac_part )
          return p;
    
        /* save position of point */
        q    = p;
        *p++ = '.';
    
        /* apply rounding */
        frac_part = frac_part * 10 + 5;
    
        /* get digits of fractional part */
        for ( i = 0; i < 5; i++ )
        {
          *p++ = '0' + (char)( frac_part / 0x10000L );
    
          frac_part %= 0x10000L;
          if ( !frac_part )
            break;
    
          frac_part *= 10;
        }
    
        /*
            If the remainder stored in `frac_part' (after the last FOR loop) is
            smaller than 34480*10, the resulting decimal value minus 0.00001 is
            an equivalent representation of `fixed'.
    
            The above FOR loop always finds the larger of the two values; I
            verified this by iterating over all possible fixed-point numbers.
    
            If the remainder is 17232*10, both values are equally good, and we
            take the next even number (following IEEE 754's `round to nearest,
            ties to even' rounding rule).
    
            If the remainder is smaller than 17232*10, the lower of the two
            numbers is nearer to the exact result (values 17232 and 34480 were
            also found by testing all possible fixed-point values).
    
            We use this to find a shorter decimal representation.  If not ending
            with digit zero, we take the representation with less error.
         */
        p--;
        if ( p - q == 5 )  /* five digits? */
        {
          /* take the representation that has zero as the last digit */
          if ( frac_part < 34480 * 10 &&
               *p == '1'              )
            *p = '0';
    
          /* otherwise use the one with less error */
          else if ( frac_part == 17232 * 10 &&
                    *p & 1                  )
            *p -= 1;
    
          else if ( frac_part < 17232 * 10 &&
                    *p != '0'              )
            *p -= 1;
        }
    
        /* remove trailing zeros */
        while ( *p == '0' )
          *p-- = '\0';
    
        return p + 1;
      }
    
    
      static const char  hexdigits[16] =
      {
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
      };
    
    
      static const char*
      sfnt_get_var_ps_name( TT_Face  face )
      {
        FT_Error   error;
        FT_Memory  memory = face->root.memory;
    
        FT_Service_MultiMasters  mm = (FT_Service_MultiMasters)face->mm;
    
        FT_UInt     num_coords;
        FT_Fixed*   coords;
        FT_MM_Var*  mm_var;
    
        FT_Int   found, win, apple;
        FT_UInt  i, j;
    
        char*  result = NULL;
        char*  p;
    
    
        if ( !face->var_postscript_prefix )
        {
          FT_UInt  len;
    
    
          /* check whether we have a Variations PostScript Name Prefix */
          found = sfnt_get_name_id( face,
                                    TT_NAME_ID_VARIATIONS_PREFIX,
                                    &win,
                                    &apple );
          if ( !found )
          {
            /* otherwise use the typographic family name */
            found = sfnt_get_name_id( face,
                                      TT_NAME_ID_TYPOGRAPHIC_FAMILY,
                                      &win,
                                      &apple );
          }
    
          if ( !found )
          {
            /* as a last resort we try the family name; note that this is */
            /* not in the Adobe TechNote, but GX fonts (which predate the */
            /* TechNote) benefit from this behaviour                      */
            found = sfnt_get_name_id( face,
                                      TT_NAME_ID_FONT_FAMILY,
                                      &win,
                                      &apple );
          }
    
          if ( !found )
          {
            FT_TRACE0(( "sfnt_get_var_ps_name:"
                        " Can't construct PS name prefix for font instances\n" ));
            return NULL;
          }
    
          /* prefer Windows entries over Apple */
          if ( win != -1 )
            result = get_win_string( face->root.memory,
                                     face->name_table.stream,
                                     face->name_table.names + win,
                                     sfnt_is_alphanumeric,
                                     0 );
          if ( !result && apple != -1 )
            result = get_apple_string( face->root.memory,
                                       face->name_table.stream,
                                       face->name_table.names + apple,
                                       sfnt_is_alphanumeric,
                                       0 );
    
          if ( !result )
          {
            FT_TRACE0(( "sfnt_get_var_ps_name:"
                        " No valid PS name prefix for font instances found\n" ));
            return NULL;
          }
    
          len = ft_strlen( result );
    
          /* sanitize if necessary; we reserve space for 36 bytes (a 128bit  */
          /* checksum as a hex number, preceded by `-' and followed by three */
          /* ASCII dots, to be used if the constructed PS name would be too  */
          /* long); this is also sufficient for a single instance            */
          if ( len > MAX_PS_NAME_LEN - ( 1 + 32 + 3 ) )
          {
            len         = MAX_PS_NAME_LEN - ( 1 + 32 + 3 );
            result[len] = '\0';
    
            FT_TRACE0(( "sfnt_get_var_ps_name:"
                        " Shortening variation PS name prefix\n" ));
            FT_TRACE0(( "                     "
                        " to %d characters\n", len ));
          }
    
          face->var_postscript_prefix     = result;
          face->var_postscript_prefix_len = len;
        }
    
        mm->get_var_blend( FT_FACE( face ),
                           &num_coords,
                           &coords,
                           NULL,
                           &mm_var );
    
        if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) &&
             !FT_IS_VARIATION( FT_FACE( face ) )     )
        {
          SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
    
          FT_Long  instance = ( ( face->root.face_index & 0x7FFF0000L ) >> 16 ) - 1;
          FT_UInt  psid     = mm_var->namedstyle[instance].psid;
    
          char*  ps_name = NULL;
    
    
          /* try first to load the name string with index `postScriptNameID' */
          if ( psid == 6                      ||
               ( psid > 255 && psid < 32768 ) )
            (void)sfnt->get_name( face, (FT_UShort)psid, &ps_name );
    
          if ( ps_name )
          {
            result = ps_name;
            p      = result + ft_strlen( result ) + 1;
    
            goto check_length;
          }
          else
          {
            /* otherwise construct a name using `subfamilyNameID' */
            FT_UInt  strid = mm_var->namedstyle[instance].strid;
    
            char*  subfamily_name;
            char*  s;
    
    
            (void)sfnt->get_name( face, (FT_UShort)strid, &subfamily_name );
    
            if ( !subfamily_name )
            {
              FT_TRACE1(( "sfnt_get_var_ps_name:"
                          " can't construct named instance PS name;\n" ));
              FT_TRACE1(( "                     "
                          " trying to construct normal instance PS name\n" ));
              goto construct_instance_name;
            }
    
            /* after the prefix we have character `-' followed by the   */
            /* subfamily name (using only characters a-z, A-Z, and 0-9) */
            if ( FT_QALLOC( result, face->var_postscript_prefix_len +
                                    1 + ft_strlen( subfamily_name ) + 1 ) )
              return NULL;
    
            ft_strcpy( result, face->var_postscript_prefix );
    
            p = result + face->var_postscript_prefix_len;
            *p++ = '-';
    
            s = subfamily_name;
            while ( *s )
            {
              if ( ft_isalnum( *s ) )
                *p++ = *s;
              s++;
            }
            *p++ = '\0';
    
            FT_FREE( subfamily_name );
          }
        }
        else
        {
          FT_Var_Axis*  axis;
    
    
        construct_instance_name:
          axis = mm_var->axis;
    
          if ( FT_QALLOC( result,
                          face->var_postscript_prefix_len +
                            num_coords * MAX_VALUE_DESCRIPTOR_LEN + 1 ) )
            return NULL;
    
          p = result;
    
          ft_strcpy( p, face->var_postscript_prefix );
          p += face->var_postscript_prefix_len;
    
          for ( i = 0; i < num_coords; i++, coords++, axis++ )
          {
            char  t;
    
    
            /* omit axis value descriptor if it is identical */
            /* to the default axis value                     */
            if ( *coords == axis->def )
              continue;
    
            *p++ = '_';
            p    = fixed2float( *coords, p );
    
            t = (char)( axis->tag >> 24 );
            if ( t != ' ' && ft_isalnum( t ) )
              *p++ = t;
            t = (char)( axis->tag >> 16 );
            if ( t != ' ' && ft_isalnum( t ) )
              *p++ = t;
            t = (char)( axis->tag >> 8 );
            if ( t != ' ' && ft_isalnum( t ) )
              *p++ = t;
            t = (char)axis->tag;
            if ( t != ' ' && ft_isalnum( t ) )
              *p++ = t;
          }
          *p++ = '\0';
        }
    
      check_length:
        if ( p - result > MAX_PS_NAME_LEN )
        {
          /* the PS name is too long; replace the part after the prefix with */
          /* a checksum; we use MurmurHash 3 with a hash length of 128 bit   */
    
          FT_UInt32  seed = 123456789;
    
          FT_UInt32   hash[4];
          FT_UInt32*  h;
    
    
          murmur_hash_3_128( result, p - result, seed, hash );
    
          p = result + face->var_postscript_prefix_len;
          *p++ = '-';
    
          /* we convert the hash value to hex digits from back to front */
          p += 32 + 3;
          h  = hash + 3;
    
          *p-- = '\0';
          *p-- = '.';
          *p-- = '.';
          *p-- = '.';
    
          for ( i = 0; i < 4; i++, h-- )
          {
            FT_UInt32  v = *h;
    
    
            for ( j = 0; j < 8; j++ )
            {
              *p--   = hexdigits[v & 0xF];
              v    >>= 4;
            }
          }
        }
    
        return result;
      }
    
    #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
    
    
      static const char*
      sfnt_get_ps_name( TT_Face  face )
      {
        FT_Int       found, win, apple;
        const char*  result = NULL;
    
    
        if ( face->postscript_name )
          return face->postscript_name;
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
        if ( face->blend                                 &&
             ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
               FT_IS_VARIATION( FT_FACE( face ) )      ) )
        {
          face->postscript_name = sfnt_get_var_ps_name( face );
          return face->postscript_name;
        }
    #endif
    
        /* scan the name table to see whether we have a Postscript name here, */
        /* either in Macintosh or Windows platform encodings                  */
        found = sfnt_get_name_id( face, TT_NAME_ID_PS_NAME, &win, &apple );
        if ( !found )
          return NULL;
    
        /* prefer Windows entries over Apple */
        if ( win != -1 )
          result = get_win_string( face->root.memory,
                                   face->name_table.stream,
                                   face->name_table.names + win,
                                   sfnt_is_postscript,
                                   1 );
        if ( !result && apple != -1 )
          result = get_apple_string( face->root.memory,
                                     face->name_table.stream,
                                     face->name_table.names + apple,
                                     sfnt_is_postscript,
                                     1 );
    
        face->postscript_name = result;
    
        return result;
      }
    
    
      FT_DEFINE_SERVICE_PSFONTNAMEREC(
        sfnt_service_ps_name,
    
        (FT_PsName_GetFunc)sfnt_get_ps_name       /* get_ps_font_name */
      )
    
    
      /*
       * TT CMAP INFO
       */
      FT_DEFINE_SERVICE_TTCMAPSREC(
        tt_service_get_cmap_info,
    
        (TT_CMap_Info_GetFunc)tt_get_cmap_info    /* get_cmap_info */
      )
    
    
    #ifdef TT_CONFIG_OPTION_BDF
    
      static FT_Error
      sfnt_get_charset_id( TT_Face       face,
                           const char*  *acharset_encoding,
                           const char*  *acharset_registry )
      {
        BDF_PropertyRec  encoding, registry;
        FT_Error         error;
    
    
        /* XXX: I don't know whether this is correct, since
         *      tt_face_find_bdf_prop only returns something correct if we have
         *      previously selected a size that is listed in the BDF table.
         *      Should we change the BDF table format to include single offsets
         *      for `CHARSET_REGISTRY' and `CHARSET_ENCODING'?
         */
        error = tt_face_find_bdf_prop( face, "CHARSET_REGISTRY", &registry );
        if ( !error )
        {
          error = tt_face_find_bdf_prop( face, "CHARSET_ENCODING", &encoding );
          if ( !error )
          {
            if ( registry.type == BDF_PROPERTY_TYPE_ATOM &&
                 encoding.type == BDF_PROPERTY_TYPE_ATOM )
            {
              *acharset_encoding = encoding.u.atom;
              *acharset_registry = registry.u.atom;
            }
            else
              error = FT_THROW( Invalid_Argument );
          }
        }
    
        return error;
      }
    
    
      FT_DEFINE_SERVICE_BDFRec(
        sfnt_service_bdf,
    
        (FT_BDF_GetCharsetIdFunc)sfnt_get_charset_id,     /* get_charset_id */
        (FT_BDF_GetPropertyFunc) tt_face_find_bdf_prop    /* get_property   */
      )
    
    
    #endif /* TT_CONFIG_OPTION_BDF */
    
    
      /*
       * SERVICE LIST
       */
    
    #if defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES && defined TT_CONFIG_OPTION_BDF
      FT_DEFINE_SERVICEDESCREC5(
        sfnt_services,
    
        FT_SERVICE_ID_SFNT_TABLE,           &sfnt_service_sfnt_table,
        FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
        FT_SERVICE_ID_GLYPH_DICT,           &sfnt_service_glyph_dict,
        FT_SERVICE_ID_BDF,                  &sfnt_service_bdf,
        FT_SERVICE_ID_TT_CMAP,              &tt_service_get_cmap_info )
    #elif defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES
      FT_DEFINE_SERVICEDESCREC4(
        sfnt_services,
    
        FT_SERVICE_ID_SFNT_TABLE,           &sfnt_service_sfnt_table,
        FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
        FT_SERVICE_ID_GLYPH_DICT,           &sfnt_service_glyph_dict,
        FT_SERVICE_ID_TT_CMAP,              &tt_service_get_cmap_info )
    #elif defined TT_CONFIG_OPTION_BDF
      FT_DEFINE_SERVICEDESCREC4(
        sfnt_services,
    
        FT_SERVICE_ID_SFNT_TABLE,           &sfnt_service_sfnt_table,
        FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
        FT_SERVICE_ID_BDF,                  &sfnt_service_bdf,
        FT_SERVICE_ID_TT_CMAP,              &tt_service_get_cmap_info )
    #else
      FT_DEFINE_SERVICEDESCREC3(
        sfnt_services,
    
        FT_SERVICE_ID_SFNT_TABLE,           &sfnt_service_sfnt_table,
        FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name,
        FT_SERVICE_ID_TT_CMAP,              &tt_service_get_cmap_info )
    #endif
    
    
      FT_CALLBACK_DEF( FT_Module_Interface )
      sfnt_get_interface( FT_Module    module,
                          const char*  module_interface )
      {
        FT_UNUSED( module );
    
        return ft_service_list_lookup( sfnt_services, module_interface );
      }
    
    
    #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    #define PUT_EMBEDDED_BITMAPS( a )  a
    #else
    #define PUT_EMBEDDED_BITMAPS( a )  NULL
    #endif
    
    #ifdef TT_CONFIG_OPTION_COLOR_LAYERS
    #define PUT_COLOR_LAYERS( a )  a
    #else
    #define PUT_COLOR_LAYERS( a )  NULL
    #endif
    
    #ifdef FT_CONFIG_OPTION_SVG
    #define PUT_SVG_SUPPORT( a )  a
    #else
    #define PUT_SVG_SUPPORT( a )  NULL
    #endif
    
    #define PUT_COLOR_LAYERS_V1( a )  PUT_COLOR_LAYERS( a )
    
    #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
    #define PUT_PS_NAMES( a )  a
    #else
    #define PUT_PS_NAMES( a )  NULL
    #endif
    
      FT_DEFINE_SFNT_INTERFACE(
        sfnt_interface,
    
        tt_face_goto_table,     /* TT_Loader_GotoTableFunc goto_table      */
    
        sfnt_init_face,         /* TT_Init_Face_Func       init_face       */
        sfnt_load_face,         /* TT_Load_Face_Func       load_face       */
        sfnt_done_face,         /* TT_Done_Face_Func       done_face       */
        sfnt_get_interface,     /* FT_Module_Requester     get_interface   */
    
        tt_face_load_any,       /* TT_Load_Any_Func        load_any        */
    
        tt_face_load_head,      /* TT_Load_Table_Func      load_head       */
        tt_face_load_hhea,      /* TT_Load_Metrics_Func    load_hhea       */
        tt_face_load_cmap,      /* TT_Load_Table_Func      load_cmap       */
        tt_face_load_maxp,      /* TT_Load_Table_Func      load_maxp       */
        tt_face_load_os2,       /* TT_Load_Table_Func      load_os2        */
        tt_face_load_post,      /* TT_Load_Table_Func      load_post       */
    
        tt_face_load_name,      /* TT_Load_Table_Func      load_name       */
        tt_face_free_name,      /* TT_Free_Table_Func      free_name       */
    
        tt_face_load_kern,      /* TT_Load_Table_Func      load_kern       */
        tt_face_load_gasp,      /* TT_Load_Table_Func      load_gasp       */
        tt_face_load_pclt,      /* TT_Load_Table_Func      load_init       */
    
        /* see `ttload.h' */
        PUT_EMBEDDED_BITMAPS( tt_face_load_bhed ),
                                /* TT_Load_Table_Func      load_bhed       */
        PUT_EMBEDDED_BITMAPS( tt_face_load_sbit_image ),
                                /* TT_Load_SBit_Image_Func load_sbit_image */
    
        /* see `ttpost.h' */
        PUT_PS_NAMES( tt_face_get_ps_name   ),
                                /* TT_Get_PS_Name_Func     get_psname      */
        PUT_PS_NAMES( tt_face_free_ps_names ),
                                /* TT_Free_Table_Func      free_psnames    */
    
        /* since version 2.1.8 */
        tt_face_get_kerning,    /* TT_Face_GetKerningFunc  get_kerning     */
    
        /* since version 2.2 */
        tt_face_load_font_dir,  /* TT_Load_Table_Func      load_font_dir   */
        tt_face_load_hmtx,      /* TT_Load_Metrics_Func    load_hmtx       */
    
        /* see `ttsbit.h' and `sfnt.h' */
        PUT_EMBEDDED_BITMAPS( tt_face_load_sbit ),
                                /* TT_Load_Table_Func      load_eblc       */
        PUT_EMBEDDED_BITMAPS( tt_face_free_sbit ),
                                /* TT_Free_Table_Func      free_eblc       */
    
        PUT_EMBEDDED_BITMAPS( tt_face_set_sbit_strike     ),
                      /* TT_Set_SBit_Strike_Func      set_sbit_strike      */
        PUT_EMBEDDED_BITMAPS( tt_face_load_strike_metrics ),
                      /* TT_Load_Strike_Metrics_Func  load_strike_metrics  */
    
        PUT_COLOR_LAYERS( tt_face_load_cpal ),
                                /* TT_Load_Table_Func      load_cpal       */
        PUT_COLOR_LAYERS( tt_face_load_colr ),
                                /* TT_Load_Table_Func      load_colr       */
        PUT_COLOR_LAYERS( tt_face_free_cpal ),
                                /* TT_Free_Table_Func      free_cpal       */
        PUT_COLOR_LAYERS( tt_face_free_colr ),
                                /* TT_Free_Table_Func      free_colr       */
        PUT_COLOR_LAYERS( tt_face_palette_set ),
                                /* TT_Set_Palette_Func     set_palette     */
        PUT_COLOR_LAYERS( tt_face_get_colr_layer ),
                                /* TT_Get_Colr_Layer_Func  get_colr_layer  */
    
        PUT_COLOR_LAYERS_V1( tt_face_get_colr_glyph_paint ),
                  /* TT_Get_Color_Glyph_Paint_Func    get_colr_glyph_paint */
        PUT_COLOR_LAYERS_V1( tt_face_get_color_glyph_clipbox ),
                  /* TT_Get_Color_Glyph_ClipBox_Func  get_clipbox          */
        PUT_COLOR_LAYERS_V1( tt_face_get_paint_layers ),
                  /* TT_Get_Paint_Layers_Func         get_paint_layers     */
        PUT_COLOR_LAYERS_V1( tt_face_get_colorline_stops ),
                  /* TT_Get_Paint                     get_paint            */
        PUT_COLOR_LAYERS_V1( tt_face_get_paint ),
                  /* TT_Get_Colorline_Stops_Func      get_colorline_stops  */
    
        PUT_COLOR_LAYERS( tt_face_colr_blend_layer ),
                                /* TT_Blend_Colr_Func      colr_blend      */
    
        tt_face_get_metrics,    /* TT_Get_Metrics_Func     get_metrics     */
    
        tt_face_get_name,       /* TT_Get_Name_Func        get_name        */
        sfnt_get_name_id,       /* TT_Get_Name_ID_Func     get_name_id     */
    
        PUT_SVG_SUPPORT( tt_face_load_svg ),
                                /* TT_Load_Table_Func      load_svg        */
        PUT_SVG_SUPPORT( tt_face_free_svg ),
                                /* TT_Free_Table_Func      free_svg        */
        PUT_SVG_SUPPORT( tt_face_load_svg_doc )
                                /* TT_Load_Svg_Doc_Func    load_svg_doc    */
      )
    
    
      FT_DEFINE_MODULE(
        sfnt_module_class,
    
        0,  /* not a font driver or renderer */
        sizeof ( FT_ModuleRec ),
    
        "sfnt",     /* driver name                            */
        0x10000L,   /* driver version 1.0                     */
        0x20000L,   /* driver requires FreeType 2.0 or higher */
    
        (const void*)&sfnt_interface,  /* module specific interface */
    
        (FT_Module_Constructor)NULL,               /* module_init   */
        (FT_Module_Destructor) NULL,               /* module_done   */
        (FT_Module_Requester)  sfnt_get_interface  /* get_interface */
      )
    
    
    /* END */