Imported Upstream version 2.13.2
[platform/upstream/freetype2.git] / src / sfnt / sfdriver.c
index 847d83d..0925940 100644 (file)
@@ -1,30 +1,29 @@
-/***************************************************************************/
-/*                                                                         */
-/*  sfdriver.c                                                             */
-/*                                                                         */
-/*    High-level SFNT driver interface (body).                             */
-/*                                                                         */
-/*  Copyright 1996-2007, 2009-2011 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 FT_INTERNAL_SFNT_H
-#include FT_INTERNAL_OBJECTS_H
+/****************************************************************************
+ *
+ * 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 "sfntpic.h"
 
 #include "sferrors.h"
 
 #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 FT_SERVICE_BDF_H
+#include <freetype/internal/services/svbdf.h>
 #endif
 
 #include "ttcmap.h"
 #include "ttkern.h"
 #include "ttmtx.h"
 
-#include FT_SERVICE_GLYPH_DICT_H
-#include FT_SERVICE_POSTSCRIPT_NAME_H
-#include FT_SERVICE_SFNT_H
-#include FT_SERVICE_TT_CMAP_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.                                            */
-  /*                                                                       */
+#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  trace_sfdriver
+#define FT_COMPONENT  sfdriver
 
 
- /*
-   SFNT TABLE SERVICE
-  *
-  */
 /*
+   * SFNT TABLE SERVICE
+   *
+   */
 
-  static void*
-  get_sfnt_table( TT_Face      face,
+  FT_CALLBACK_DEF( FT_Error )
+  sfnt_load_table( FT_Face    face,    /* TT_Face */
+                   FT_ULong   tag,
+                   FT_Long    offset,
+                   FT_Byte*   buffer,
+                   FT_ULong*  length )
+  {
+    TT_Face  ttface = (TT_Face)face;
+
+
+    return tt_face_load_any( ttface, tag, offset, buffer, length );
+  }
+
+
+  FT_CALLBACK_DEF( void* )
+  get_sfnt_table( FT_Face      face,  /* TT_Face */
                   FT_Sfnt_Tag  tag )
   {
+    TT_Face  ttface = (TT_Face)face;
+
     void*  table;
 
 
     switch ( tag )
     {
-    case ft_sfnt_head:
-      table = &face->header;
+    case FT_SFNT_HEAD:
+      table = &ttface->header;
       break;
 
-    case ft_sfnt_hhea:
-      table = &face->horizontal;
+    case FT_SFNT_HHEA:
+      table = &ttface->horizontal;
       break;
 
-    case ft_sfnt_vhea:
-      table = face->vertical_info ? &face->vertical : 0;
+    case FT_SFNT_VHEA:
+      table = ttface->vertical_info ? &ttface->vertical : NULL;
       break;
 
-    case ft_sfnt_os2:
-      table = face->os2.version == 0xFFFFU ? 0 : &face->os2;
+    case FT_SFNT_OS2:
+      table = ( ttface->os2.version == 0xFFFFU ) ? NULL : &ttface->os2;
       break;
 
-    case ft_sfnt_post:
-      table = &face->postscript;
+    case FT_SFNT_POST:
+      table = &ttface->postscript;
       break;
 
-    case ft_sfnt_maxp:
-      table = &face->max_profile;
+    case FT_SFNT_MAXP:
+      table = &ttface->max_profile;
       break;
 
-    case ft_sfnt_pclt:
-      table = face->pclt.Version ? &face->pclt : 0;
+    case FT_SFNT_PCLT:
+      table = ttface->pclt.Version ? &ttface->pclt : NULL;
       break;
 
     default:
-      table = 0;
+      table = NULL;
     }
 
     return table;
   }
 
 
-  static FT_Error
-  sfnt_table_info( TT_Face    face,
+  FT_CALLBACK_DEF( FT_Error )
+  sfnt_table_info( FT_Face    face,    /* TT_Face */
                    FT_UInt    idx,
                    FT_ULong  *tag,
                    FT_ULong  *offset,
                    FT_ULong  *length )
   {
+    TT_Face  ttface = (TT_Face)face;
+
+
     if ( !offset || !length )
-      return SFNT_Err_Invalid_Argument;
+      return FT_THROW( Invalid_Argument );
 
     if ( !tag )
-      *length = face->num_tables;
+      *length = ttface->num_tables;
     else
     {
-      if ( idx >= face->num_tables )
-        return SFNT_Err_Table_Missing;
+      if ( idx >= ttface->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;
+      *tag    = ttface->dir_tables[idx].Tag;
+      *offset = ttface->dir_tables[idx].Offset;
+      *length = ttface->dir_tables[idx].Length;
     }
 
-    return SFNT_Err_Ok;
+    return FT_Err_Ok;
   }
 
 
-  FT_DEFINE_SERVICE_SFNT_TABLEREC(sfnt_service_sfnt_table,
-    (FT_SFNT_TableLoadFunc)tt_face_load_any,
-    (FT_SFNT_TableGetFunc) get_sfnt_table,
-    (FT_SFNT_TableInfoFunc)sfnt_table_info
+  FT_DEFINE_SERVICE_SFNT_TABLEREC(
+    sfnt_service_sfnt_table,
+
+    sfnt_load_table,  /* FT_SFNT_TableLoadFunc load_table */
+    get_sfnt_table,   /* FT_SFNT_TableGetFunc  get_table  */
+    sfnt_table_info   /* FT_SFNT_TableInfoFunc table_info */
   )
 
 
 #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
 
- /*
-   GLYPH DICT SERVICE
-  *
-  */
 /*
+   * GLYPH DICT SERVICE
+   *
+   */
 
-  static FT_Error
-  sfnt_get_glyph_name( TT_Face     face,
+  FT_CALLBACK_DEF( FT_Error )
+  sfnt_get_glyph_name( FT_Face     face,
                        FT_UInt     glyph_index,
                        FT_Pointer  buffer,
                        FT_UInt     buffer_max )
     FT_Error    error;
 
 
-    error = tt_face_get_ps_name( face, glyph_index, &gname );
+    error = tt_face_get_ps_name( (TT_Face)face, glyph_index, &gname );
     if ( !error )
       FT_STRCPYN( buffer, gname, buffer_max );
 
   }
 
 
-  static FT_UInt
-  sfnt_get_name_index( TT_Face     face,
-                       FT_String*  glyph_name )
+  FT_CALLBACK_DEF( FT_UInt )
+  sfnt_get_name_index( FT_Face           face,
+                       const FT_String*  glyph_name )
   {
-    FT_Face   root = &face->root;
-    FT_UInt   i, max_gid = FT_UINT_MAX;
+    TT_Face  ttface = (TT_Face)face;
+
+    FT_UInt  i, max_gid = FT_UINT_MAX;
 
 
-    if ( root->num_glyphs < 0 )
+    if ( face->num_glyphs < 0 )
       return 0;
-    else if ( ( FT_ULong ) root->num_glyphs < FT_UINT_MAX )
-      max_gid = ( FT_UInt ) root->num_glyphs;
+    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%08x\n",
-         FT_UINT_MAX, root->num_glyphs ));
+      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( face, i, &gname );
+      FT_Error    error = tt_face_get_ps_name( ttface, i, &gname );
 
 
       if ( error )
   }
 
 
-  FT_DEFINE_SERVICE_GLYPHDICTREC(sfnt_service_glyph_dict,
-    (FT_GlyphDict_GetNameFunc)  sfnt_get_glyph_name,
-    (FT_GlyphDict_NameIndexFunc)sfnt_get_name_index
+  FT_DEFINE_SERVICE_GLYPHDICTREC(
+    sfnt_service_glyph_dict,
+
+    sfnt_get_glyph_name,  /* FT_GlyphDict_GetNameFunc   get_name   */
+    sfnt_get_name_index   /* FT_GlyphDict_NameIndexFunc name_index */
   )
 
 #endif /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
 
 
- /*
-   POSTSCRIPT NAME SERVICE
-  *
-  */
 /*
+   * POSTSCRIPT NAME SERVICE
+   *
+   */
 
-  static const char*
-  sfnt_get_ps_name( TT_Face  face )
+  /* an array representing allowed ASCII characters in a PS string */
+  static const unsigned char sfnt_ps_map[16] =
   {
-    FT_Int       n, found_win, found_apple;
-    const char*  result = NULL;
+                /*             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;
 
 
-    /* shouldn't happen, but just in case to avoid memory leaks */
-    if ( face->postscript_name )
-      return face->postscript_name;
+    if ( c < 0 || c >= 0x80 )
+      return 0;
 
-    /* scan the name table to see whether we have a Postscript name here, */
-    /* either in Macintosh or Windows platform encodings                  */
-    found_win   = -1;
-    found_apple = -1;
+    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] ));
+        continue;
+      }
+    }
+    *r = '\0';
+
+    FT_FRAME_EXIT();
+
+    if ( r != result )
+      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 ));
+        continue;
+      }
+    }
+    *r = '\0';
+
+    FT_FRAME_EXIT();
+
+    if ( r != result )
+      return result;
+
+  get_apple_string_error:
+    FT_FREE( result );
+
+    entry->stringOffset = 0;
+    entry->stringLength = 0;
+    FT_FREE( entry->string );
+
+    return NULL;
+  }
+
+
+  FT_CALLBACK_DEF( 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_NameEntryRec*  name = face->name_table.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 ( name->nameID == 6 && name->stringLength > 0 )
+    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 )
+      {
+        /* according to the 'name' documentation in the OpenType   */
+        /* specification the font family name is to be used if the */
+        /* typographic family name is missing, so let's do that    */
+        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" ));
+        /* XXX It probably makes sense to never let this fail */
+        /*     since an arbitrary prefix should work, too.    */
+        /*     On the other hand, it is very unlikely that    */
+        /*     we ever reach this code at all.                */
+        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 ) )
       {
-        if ( name->platformID == 3     &&
-             name->encodingID == 1     &&
-             name->languageID == 0x409 )
-          found_win = n;
-
-        if ( name->platformID == 1 &&
-             name->encodingID == 0 &&
-             name->languageID == 0 )
-          found_apple = n;
+        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;
     }
 
-    if ( found_win != -1 )
+    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 ) )     )
     {
-      FT_Memory         memory = face->root.memory;
-      TT_NameEntryRec*  name   = face->name_table.names + found_win;
-      FT_UInt           len    = name->stringLength / 2;
-      FT_Error          error  = SFNT_Err_Ok;
+      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;
+
 
-      FT_UNUSED( error );
+      /* 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;
 
-      if ( !FT_ALLOC( result, name->stringLength + 1 ) )
+        goto check_length;
+      }
+      else
       {
-        FT_Stream   stream = face->name_table.stream;
-        FT_String*  r      = (FT_String*)result;
-        FT_Byte*    p      = (FT_Byte*)name->string;
+        /* otherwise construct a name using `subfamilyNameID' */
+        FT_UInt  strid = mm_var->namedstyle[instance].strid;
 
+        char*  subfamily_name;
+        char*  s;
 
-        if ( FT_STREAM_SEEK( name->stringOffset ) ||
-             FT_FRAME_ENTER( name->stringLength ) )
-        {
-          FT_FREE( result );
-          name->stringLength = 0;
-          name->stringOffset = 0;
-          FT_FREE( name->string );
 
-          goto Exit;
+        (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;
         }
 
-        p = (FT_Byte*)stream->cursor;
+        /* 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 );
 
-        for ( ; len > 0; len--, p += 2 )
+        p = result + face->var_postscript_prefix_len;
+        *p++ = '-';
+
+        s = subfamily_name;
+        while ( *s )
         {
-          if ( p[0] == 0 && p[1] >= 32 && p[1] < 128 )
-            *r++ = p[1];
+          if ( ft_isalnum( *s ) )
+            *p++ = *s;
+          s++;
         }
-        *r = '\0';
+        *p++ = '\0';
 
-        FT_FRAME_EXIT();
+        FT_FREE( subfamily_name );
       }
-      goto Exit;
     }
+    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;
 
-    if ( found_apple != -1 )
+      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 )
     {
-      FT_Memory         memory = face->root.memory;
-      TT_NameEntryRec*  name   = face->name_table.names + found_apple;
-      FT_UInt           len    = name->stringLength;
-      FT_Error          error  = SFNT_Err_Ok;
+      /* 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++ = '-';
 
-      FT_UNUSED( error );
+      /* we convert the hash value to hex digits from back to front */
+      p += 32 + 3;
+      h  = hash + 3;
 
+      *p-- = '\0';
+      *p-- = '.';
+      *p-- = '.';
+      *p-- = '.';
 
-      if ( !FT_ALLOC( result, len + 1 ) )
+      for ( i = 0; i < 4; i++, h-- )
       {
-        FT_Stream  stream = face->name_table.stream;
+        FT_UInt32  v = *h;
 
 
-        if ( FT_STREAM_SEEK( name->stringOffset ) ||
-             FT_STREAM_READ( result, len )        )
+        for ( j = 0; j < 8; j++ )
         {
-          name->stringOffset = 0;
-          name->stringLength = 0;
-          FT_FREE( name->string );
-          FT_FREE( result );
-          goto Exit;
+          *p--   = hexdigits[v & 0xF];
+          v    >>= 4;
         }
-        ((char*)result)[len] = '\0';
       }
     }
 
-  Exit:
-    face->postscript_name = result;
     return result;
   }
 
-  FT_DEFINE_SERVICE_PSFONTNAMEREC(sfnt_service_ps_name,
-    (FT_PsName_GetFunc)sfnt_get_ps_name
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+  FT_CALLBACK_DEF( const char* )
+  sfnt_get_ps_name( FT_Face  face )    /* TT_Face */
+  {
+    TT_Face  ttface = (TT_Face)face;
+
+    FT_Int       found, win, apple;
+    const char*  result = NULL;
+
+
+    if ( ttface->postscript_name )
+      return ttface->postscript_name;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    if ( ttface->blend                    &&
+         ( FT_IS_NAMED_INSTANCE( face ) ||
+           FT_IS_VARIATION( face )      ) )
+    {
+      ttface->postscript_name = sfnt_get_var_ps_name( ttface );
+      return ttface->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( ttface, TT_NAME_ID_PS_NAME, &win, &apple );
+    if ( !found )
+      return NULL;
+
+    /* prefer Windows entries over Apple */
+    if ( win != -1 )
+      result = get_win_string( FT_FACE_MEMORY( face ),
+                               ttface->name_table.stream,
+                               ttface->name_table.names + win,
+                               sfnt_is_postscript,
+                               1 );
+    if ( !result && apple != -1 )
+      result = get_apple_string( FT_FACE_MEMORY( face ),
+                                 ttface->name_table.stream,
+                                 ttface->name_table.names + apple,
+                                 sfnt_is_postscript,
+                                 1 );
+
+    ttface->postscript_name = result;
+
+    return result;
+  }
+
+
+  FT_DEFINE_SERVICE_PSFONTNAMEREC(
+    sfnt_service_ps_name,
+
+    sfnt_get_ps_name  /* FT_PsName_GetFunc get_ps_font_name */
   )
 
 
   /*
-   *  TT CMAP INFO
+   * TT CMAP INFO
    */
-  FT_DEFINE_SERVICE_TTCMAPSREC(tt_service_get_cmap_info,
-    (TT_CMap_Info_GetFunc)tt_get_cmap_info
+  FT_DEFINE_SERVICE_TTCMAPSREC(
+    tt_service_get_cmap_info,
+
+    tt_get_cmap_info  /* TT_CMap_Info_GetFunc get_cmap_info */
   )
 
 
 #ifdef TT_CONFIG_OPTION_BDF
 
   static FT_Error
-  sfnt_get_charset_id( TT_Face       face,
+  sfnt_get_charset_id( FT_Face       face,
                        const char*  *acharset_encoding,
                        const char*  *acharset_registry )
   {
           *acharset_registry = registry.u.atom;
         }
         else
-          error = SFNT_Err_Invalid_Argument;
+          error = FT_THROW( Invalid_Argument );
       }
     }
 
   }
 
 
-  FT_DEFINE_SERVICE_BDFRec(sfnt_service_bdf,
-    (FT_BDF_GetCharsetIdFunc) sfnt_get_charset_id,
-    (FT_BDF_GetPropertyFunc)  tt_face_find_bdf_prop
+  FT_DEFINE_SERVICE_BDFRec(
+    sfnt_service_bdf,
+
+    sfnt_get_charset_id,   /* FT_BDF_GetCharsetIdFunc get_charset_id */
+    tt_face_find_bdf_prop  /* FT_BDF_GetPropertyFunc  get_property   */
   )
 
+
 #endif /* TT_CONFIG_OPTION_BDF */
 
 
   /*
-   *  SERVICE LIST
+   * SERVICE LIST
    */
 
 #if defined TT_CONFIG_OPTION_POSTSCRIPT_NAMES && defined TT_CONFIG_OPTION_BDF
-  FT_DEFINE_SERVICEDESCREC5(sfnt_services,
-    FT_SERVICE_ID_SFNT_TABLE,           &FT_SFNT_SERVICE_SFNT_TABLE_GET,
-    FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &FT_SFNT_SERVICE_PS_NAME_GET,
-    FT_SERVICE_ID_GLYPH_DICT,           &FT_SFNT_SERVICE_GLYPH_DICT_GET,
-    FT_SERVICE_ID_BDF,                  &FT_SFNT_SERVICE_BDF_GET,
-    FT_SERVICE_ID_TT_CMAP,              &FT_TT_SERVICE_GET_CMAP_INFO_GET
-  )
+  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,           &FT_SFNT_SERVICE_SFNT_TABLE_GET,
-    FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &FT_SFNT_SERVICE_PS_NAME_GET,
-    FT_SERVICE_ID_GLYPH_DICT,           &FT_SFNT_SERVICE_GLYPH_DICT_GET,
-    FT_SERVICE_ID_TT_CMAP,              &FT_TT_SERVICE_GET_CMAP_INFO_GET
-  )
+  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,           &FT_SFNT_SERVICE_SFNT_TABLE_GET,
-    FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &FT_SFNT_SERVICE_PS_NAME_GET,
-    FT_SERVICE_ID_BDF,                  &FT_SFNT_SERVICE_BDF_GET,
-    FT_SERVICE_ID_TT_CMAP,              &FT_TT_SERVICE_GET_CMAP_INFO_GET
-  )
+  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,           &FT_SFNT_SERVICE_SFNT_TABLE_GET,
-    FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &FT_SFNT_SERVICE_PS_NAME_GET,
-    FT_SERVICE_ID_TT_CMAP,              &FT_TT_SERVICE_GET_CMAP_INFO_GET
-  )
+  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
 
 
   sfnt_get_interface( FT_Module    module,
                       const char*  module_interface )
   {
-    /* FT_SFNT_SERVICES_GET derefers `library' in PIC mode */
-#ifdef FT_CONFIG_OPTION_PIC
-    FT_Library  library;
-
-
-    if ( !module )
-      return NULL;
-    library = module->library;
-    if ( !library )
-      return NULL;
-#else
     FT_UNUSED( module );
-#endif
-    return ft_service_list_lookup( FT_SFNT_SERVICES_GET, module_interface );
-  }
-
-
-#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
-
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_load_sfnt_header_stub( TT_Face      face,
-                                 FT_Stream    stream,
-                                 FT_Long      face_index,
-                                 SFNT_Header  header )
-  {
-    FT_UNUSED( face );
-    FT_UNUSED( stream );
-    FT_UNUSED( face_index );
-    FT_UNUSED( header );
-
-    return SFNT_Err_Unimplemented_Feature;
-  }
-
-
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_load_directory_stub( TT_Face      face,
-                               FT_Stream    stream,
-                               SFNT_Header  header )
-  {
-    FT_UNUSED( face );
-    FT_UNUSED( stream );
-    FT_UNUSED( header );
-
-    return SFNT_Err_Unimplemented_Feature;
-  }
-
-
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_load_hdmx_stub( TT_Face    face,
-                          FT_Stream  stream )
-  {
-    FT_UNUSED( face );
-    FT_UNUSED( stream );
-
-    return SFNT_Err_Unimplemented_Feature;
-  }
-
-
-  FT_CALLBACK_DEF( void )
-  tt_face_free_hdmx_stub( TT_Face  face )
-  {
-    FT_UNUSED( face );
-  }
-
-
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_set_sbit_strike_stub( TT_Face    face,
-                                FT_UInt    x_ppem,
-                                FT_UInt    y_ppem,
-                                FT_ULong*  astrike_index )
-  {
-    /*
-     * We simply forge a FT_Size_Request and call the real function
-     * that does all the work.
-     *
-     * This stub might be called by libXfont in the X.Org Xserver,
-     * compiled against version 2.1.8 or newer.
-     */
-
-    FT_Size_RequestRec  req;
-
-
-    req.type           = FT_SIZE_REQUEST_TYPE_NOMINAL;
-    req.width          = (FT_F26Dot6)x_ppem;
-    req.height         = (FT_F26Dot6)y_ppem;
-    req.horiResolution = 0;
-    req.vertResolution = 0;
-
-    *astrike_index = 0x7FFFFFFFUL;
-
-    return tt_face_set_sbit_strike( face, &req, astrike_index );
-  }
-
-
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_load_sbit_stub( TT_Face    face,
-                          FT_Stream  stream )
-  {
-    FT_UNUSED( face );
-    FT_UNUSED( stream );
-
-    /*
-     *  This function was originally implemented to load the sbit table.
-     *  However, it has been replaced by `tt_face_load_eblc', and this stub
-     *  is only there for some rogue clients which would want to call it
-     *  directly (which doesn't make much sense).
-     */
-    return SFNT_Err_Unimplemented_Feature;
-  }
-
-
-  FT_CALLBACK_DEF( void )
-  tt_face_free_sbit_stub( TT_Face  face )
-  {
-    /* nothing to do in this stub */
-    FT_UNUSED( face );
-  }
-
-
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_load_charmap_stub( TT_Face    face,
-                             void*      cmap,
-                             FT_Stream  input )
-  {
-    FT_UNUSED( face );
-    FT_UNUSED( cmap );
-    FT_UNUSED( input );
 
-    return SFNT_Err_Unimplemented_Feature;
+    return ft_service_list_lookup( sfnt_services, module_interface );
   }
 
 
-  FT_CALLBACK_DEF( FT_Error )
-  tt_face_free_charmap_stub( TT_Face  face,
-                             void*    cmap )
-  {
-    FT_UNUSED( face );
-    FT_UNUSED( cmap );
-
-    return SFNT_Err_Ok;
-  }
-
-#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
-
 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
-#define PUT_EMBEDDED_BITMAPS(a) a
+#define PUT_EMBEDDED_BITMAPS( a )  a
 #else
-#define PUT_EMBEDDED_BITMAPS(a) 0
+#define PUT_EMBEDDED_BITMAPS( a )  NULL
 #endif
-#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
-#define PUT_PS_NAMES(a) a
+
+#ifdef TT_CONFIG_OPTION_COLOR_LAYERS
+#define PUT_COLOR_LAYERS( a )  a
 #else
-#define PUT_PS_NAMES(a) 0
+#define PUT_COLOR_LAYERS( a )  NULL
 #endif
 
-  FT_DEFINE_SFNT_INTERFACE(sfnt_interface,
-    tt_face_goto_table,
-
-    sfnt_init_face,
-    sfnt_load_face,
-    sfnt_done_face,
-    sfnt_get_interface,
-
-    tt_face_load_any,
+#ifdef FT_CONFIG_OPTION_SVG
+#define PUT_SVG_SUPPORT( a )  a
+#else
+#define PUT_SVG_SUPPORT( a )  NULL
+#endif
 
-    tt_face_load_sfnt_header_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
-    tt_face_load_directory_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
+#define PUT_COLOR_LAYERS_V1( a )  PUT_COLOR_LAYERS( a )
 
-    tt_face_load_head,
-    tt_face_load_hhea,
-    tt_face_load_cmap,
-    tt_face_load_maxp,
-    tt_face_load_os2,
-    tt_face_load_post,
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+#define PUT_PS_NAMES( a )  a
+#else
+#define PUT_PS_NAMES( a )  NULL
+#endif
 
-    tt_face_load_name,
-    tt_face_free_name,
+  FT_DEFINE_SFNT_INTERFACE(
+    sfnt_interface,
 
-    tt_face_load_hdmx_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
-    tt_face_free_hdmx_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
+    tt_face_goto_table,     /* TT_Loader_GotoTableFunc goto_table      */
 
-    tt_face_load_kern,
-    tt_face_load_gasp,
-    tt_face_load_pclt,
+    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   */
 
-    /* see `ttload.h' */
-    PUT_EMBEDDED_BITMAPS(tt_face_load_bhed),
+    tt_face_load_any,       /* TT_Load_Any_Func        load_any        */
 
-    tt_face_set_sbit_strike_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
-    tt_face_load_sbit_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
+    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_find_sbit_image, /* FT_CONFIG_OPTION_OLD_INTERNALS */
-    tt_load_sbit_metrics, /* FT_CONFIG_OPTION_OLD_INTERNALS */
+    tt_face_load_name,      /* TT_Load_Table_Func      load_name       */
+    tt_face_free_name,      /* TT_Free_Table_Func      free_name       */
 
-    PUT_EMBEDDED_BITMAPS(tt_face_load_sbit_image),
+    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       */
 
-    tt_face_free_sbit_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
+    /* 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),
-    PUT_PS_NAMES(tt_face_free_ps_names),
-
-    tt_face_load_charmap_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
-    tt_face_free_charmap_stub, /* FT_CONFIG_OPTION_OLD_INTERNALS */
+    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_get_kerning,    /* TT_Face_GetKerningFunc  get_kerning     */
 
     /* since version 2.2 */
-
-    tt_face_load_font_dir,
-    tt_face_load_hmtx,
+    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_eblc),
-    PUT_EMBEDDED_BITMAPS(tt_face_free_eblc),
-
-    PUT_EMBEDDED_BITMAPS(tt_face_set_sbit_strike),
-    PUT_EMBEDDED_BITMAPS(tt_face_load_strike_metrics),
-
-    tt_face_get_metrics
+    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,
+  FT_DEFINE_MODULE(
+    sfnt_module_class,
 
     0,  /* not a font driver or renderer */
     sizeof ( FT_ModuleRec ),
     0x10000L,   /* driver version 1.0                     */
     0x20000L,   /* driver requires FreeType 2.0 or higher */
 
-    (const void*)&FT_SFNT_INTERFACE_GET,  /* module specific interface */
+    (const void*)&sfnt_interface,  /* module specific interface */
 
-    (FT_Module_Constructor)0,
-    (FT_Module_Destructor) 0,
-    (FT_Module_Requester)  sfnt_get_interface
+    NULL,               /* FT_Module_Constructor module_init   */
+    NULL,               /* FT_Module_Destructor  module_done   */
+    sfnt_get_interface  /* FT_Module_Requester   get_interface */
   )