text:
/****************************************************************************
 *
 * t1driver.c
 *
 *   Type 1 driver interface (body).
 *
 * Copyright (C) 1996-2020 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 "t1driver.h"
#include "t1gload.h"
#include "t1load.h"

#include "t1errors.h"

#ifndef T1_CONFIG_OPTION_NO_AFM
#include "t1afm.h"
#endif

#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_HASH_H
#include FT_INTERNAL_POSTSCRIPT_PROPS_H
#include FT_DRIVER_H

#include FT_SERVICE_MULTIPLE_MASTERS_H
#include FT_SERVICE_GLYPH_DICT_H
#include FT_SERVICE_FONT_FORMAT_H
#include FT_SERVICE_POSTSCRIPT_NAME_H
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
#include FT_SERVICE_POSTSCRIPT_INFO_H
#include FT_SERVICE_PROPERTIES_H
#include FT_SERVICE_KERNING_H

  /**************************************************************************
   *
   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   * messages during execution.
   */
#undef  FT_COMPONENT
#define FT_COMPONENT  t1driver

  /*
   * GLYPH DICT SERVICE
   *
   */

  static FT_Error
  t1_get_glyph_name( T1_Face     face,
                     FT_UInt     glyph_index,
                     FT_Pointer  buffer,
                     FT_UInt     buffer_max )
  {
    FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max );

    return FT_Err_Ok;
  }

  static FT_UInt
  t1_get_name_index( T1_Face           face,
                     const FT_String*  glyph_name )
  {
    FT_Int  i;

    for ( i = 0; i < face->type1.num_glyphs; i++ )
    {
      FT_String*  gname = face->type1.glyph_names[i];

      if ( !ft_strcmp( glyph_name, gname ) )
        return (FT_UInt)i;
    }

    return 0;
  }

  static const FT_Service_GlyphDictRec  t1_service_glyph_dict =
  {
    (FT_GlyphDict_GetNameFunc)  t1_get_glyph_name,    /* get_name   */
    (FT_GlyphDict_NameIndexFunc)t1_get_name_index     /* name_index */
  };

  /*
   * POSTSCRIPT NAME SERVICE
   *
   */

  static const char*
  t1_get_ps_name( T1_Face  face )
  {
    return (const char*) face->type1.font_name;
  }

  static const FT_Service_PsFontNameRec  t1_service_ps_name =
  {
    (FT_PsName_GetFunc)t1_get_ps_name     /* get_ps_font_name */
  };

  /*
   * MULTIPLE MASTERS SERVICE
   *
   */

#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
  static const FT_Service_MultiMastersRec  t1_service_multi_masters =
  {
    (FT_Get_MM_Func)             T1_Get_Multi_Master,    /* get_mm              */
    (FT_Set_MM_Design_Func)      T1_Set_MM_Design,       /* set_mm_design       */
    (FT_Set_MM_Blend_Func)       T1_Set_MM_Blend,        /* set_mm_blend        */
    (FT_Get_MM_Blend_Func)       T1_Get_MM_Blend,        /* get_mm_blend        */
    (FT_Get_MM_Var_Func)         T1_Get_MM_Var,          /* get_mm_var          */
    (FT_Set_Var_Design_Func)     T1_Set_Var_Design,      /* set_var_design      */
    (FT_Get_Var_Design_Func)     T1_Get_Var_Design,      /* get_var_design      */
    (FT_Set_Instance_Func)       T1_Reset_MM_Blend,      /* set_instance        */
    (FT_Set_MM_WeightVector_Func)T1_Set_MM_WeightVector, /* set_mm_weightvector */
    (FT_Get_MM_WeightVector_Func)T1_Get_MM_WeightVector, /* get_mm_weightvector */

    (FT_Get_Var_Blend_Func)      NULL,                   /* get_var_blend       */
    (FT_Done_Blend_Func)         T1_Done_Blend           /* done_blend          */
  };
#endif

  /*
   * POSTSCRIPT INFO SERVICE
   *
   */

  static FT_Error
  t1_ps_get_font_info( FT_Face          face,
                       PS_FontInfoRec*  afont_info )
  {
    *afont_info = ((T1_Face)face)->type1.font_info;

    return FT_Err_Ok;
  }

  static FT_Error
  t1_ps_get_font_extra( FT_Face           face,
                        PS_FontExtraRec*  afont_extra )
  {
    *afont_extra = ((T1_Face)face)->type1.font_extra;

    return FT_Err_Ok;
  }

  static FT_Int
  t1_ps_has_glyph_names( FT_Face  face )
  {
    FT_UNUSED( face );

    return 1;
  }

  static FT_Error
  t1_ps_get_font_private( FT_Face         face,
                          PS_PrivateRec*  afont_private )
  {
    *afont_private = ((T1_Face)face)->type1.private_dict;

    return FT_Err_Ok;
  }

  static FT_Long
  t1_ps_get_font_value( FT_Face       face,
                        PS_Dict_Keys  key,
                        FT_UInt       idx,
                        void         *value,
                        FT_Long       value_len_ )
  {
    FT_ULong  retval    = 0; /* always >= 1 if valid */
    FT_ULong  value_len = value_len_ < 0 ? 0 : (FT_ULong)value_len_;

    T1_Face  t1face = (T1_Face)face;
    T1_Font  type1  = &t1face->type1;

    switch ( key )
    {
    case PS_DICT_FONT_TYPE:
      retval = sizeof ( type1->font_type );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->font_type;
      break;

    case PS_DICT_FONT_MATRIX:
      if ( idx < sizeof ( type1->font_matrix ) /
                   sizeof ( type1->font_matrix.xx ) )
      {
        FT_Fixed  val = 0;

        retval = sizeof ( val );
        if ( value && value_len >= retval )
        {
          switch ( idx )
          {
          case 0:
            val = type1->font_matrix.xx;
            break;
          case 1:
            val = type1->font_matrix.xy;
            break;
          case 2:
            val = type1->font_matrix.yx;
            break;
          case 3:
            val = type1->font_matrix.yy;
            break;
          }
          *((FT_Fixed *)value) = val;
        }
      }
      break;

    case PS_DICT_FONT_BBOX:
      if ( idx < sizeof ( type1->font_bbox ) /
                   sizeof ( type1->font_bbox.xMin ) )
      {
        FT_Fixed  val = 0;

        retval = sizeof ( val );
        if ( value && value_len >= retval )
        {
          switch ( idx )
          {
          case 0:
            val = type1->font_bbox.xMin;
            break;
          case 1:
            val = type1->font_bbox.yMin;
            break;
          case 2:
            val = type1->font_bbox.xMax;
            break;
          case 3:
            val = type1->font_bbox.yMax;
            break;
          }
          *((FT_Fixed *)value) = val;
        }
      }
      break;

    case PS_DICT_PAINT_TYPE:
      retval = sizeof ( type1->paint_type );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->paint_type;
      break;

    case PS_DICT_FONT_NAME:
      if ( type1->font_name )
      {
        retval = ft_strlen( type1->font_name ) + 1;
        if ( value && value_len >= retval )
          ft_memcpy( value, (void *)( type1->font_name ), retval );
      }
      break;

    case PS_DICT_UNIQUE_ID:
      retval = sizeof ( type1->private_dict.unique_id );
      if ( value && value_len >= retval )
        *((FT_Int *)value) = type1->private_dict.unique_id;
      break;

    case PS_DICT_NUM_CHAR_STRINGS:
      retval = sizeof ( type1->num_glyphs );
      if ( value && value_len >= retval )
        *((FT_Int *)value) = type1->num_glyphs;
      break;

    case PS_DICT_CHAR_STRING_KEY:
      if ( idx < (FT_UInt)type1->num_glyphs )
      {
        retval = ft_strlen( type1->glyph_names[idx] ) + 1;
        if ( value && value_len >= retval )
        {
          ft_memcpy( value, (void *)( type1->glyph_names[idx] ), retval );
          ((FT_Char *)value)[retval - 1] = (FT_Char)'\0';
        }
      }
      break;

    case PS_DICT_CHAR_STRING:
      if ( idx < (FT_UInt)type1->num_glyphs )
      {
        retval = type1->charstrings_len[idx] + 1;
        if ( value && value_len >= retval )
        {
          ft_memcpy( value, (void *)( type1->charstrings[idx] ),
                     retval - 1 );
          ((FT_Char *)value)[retval - 1] = (FT_Char)'\0';
        }
      }
      break;

    case PS_DICT_ENCODING_TYPE:
      retval = sizeof ( type1->encoding_type );
      if ( value && value_len >= retval )
        *((T1_EncodingType *)value) = type1->encoding_type;
      break;

    case PS_DICT_ENCODING_ENTRY:
      if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY &&
           idx < (FT_UInt)type1->encoding.num_chars       )
      {
        retval = ft_strlen( type1->encoding.char_name[idx] ) + 1;
        if ( value && value_len >= retval )
        {
          ft_memcpy( value, (void *)( type1->encoding.char_name[idx] ),
                     retval - 1 );
          ((FT_Char *)value)[retval - 1] = (FT_Char)'\0';
        }
      }
      break;

    case PS_DICT_NUM_SUBRS:
      retval = sizeof ( type1->num_subrs );
      if ( value && value_len >= retval )
        *((FT_Int *)value) = type1->num_subrs;
      break;

    case PS_DICT_SUBR:
      {
        FT_Bool  ok = 0;

        if ( type1->subrs_hash )
        {
          /* convert subr index to array index */
          size_t*  val = ft_hash_num_lookup( (FT_Int)idx,
                                             type1->subrs_hash );

          if ( val )
          {
            idx = *val;
            ok  = 1;
          }
        }
        else
        {
          if ( idx < (FT_UInt)type1->num_subrs )
            ok = 1;
        }

        if ( ok && type1->subrs )
        {
          retval = type1->subrs_len[idx] + 1;
          if ( value && value_len >= retval )
          {
            ft_memcpy( value, (void *)( type1->subrs[idx] ), retval - 1 );
            ((FT_Char *)value)[retval - 1] = (FT_Char)'\0';
          }
        }
      }
      break;

    case PS_DICT_STD_HW:
      retval = sizeof ( type1->private_dict.standard_width[0] );
      if ( value && value_len >= retval )
        *((FT_UShort *)value) = type1->private_dict.standard_width[0];
      break;

    case PS_DICT_STD_VW:
      retval = sizeof ( type1->private_dict.standard_height[0] );
      if ( value && value_len >= retval )
        *((FT_UShort *)value) = type1->private_dict.standard_height[0];
      break;

    case PS_DICT_NUM_BLUE_VALUES:
      retval = sizeof ( type1->private_dict.num_blue_values );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->private_dict.num_blue_values;
      break;

    case PS_DICT_BLUE_VALUE:
      if ( idx < type1->private_dict.num_blue_values )
      {
        retval = sizeof ( type1->private_dict.blue_values[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.blue_values[idx];
      }
      break;

    case PS_DICT_BLUE_SCALE:
      retval = sizeof ( type1->private_dict.blue_scale );
      if ( value && value_len >= retval )
        *((FT_Fixed *)value) = type1->private_dict.blue_scale;
      break;

    case PS_DICT_BLUE_FUZZ:
      retval = sizeof ( type1->private_dict.blue_fuzz );
      if ( value && value_len >= retval )
        *((FT_Int *)value) = type1->private_dict.blue_fuzz;
      break;

    case PS_DICT_BLUE_SHIFT:
      retval = sizeof ( type1->private_dict.blue_shift );
      if ( value && value_len >= retval )
        *((FT_Int *)value) = type1->private_dict.blue_shift;
      break;

    case PS_DICT_NUM_OTHER_BLUES:
      retval = sizeof ( type1->private_dict.num_other_blues );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->private_dict.num_other_blues;
      break;

    case PS_DICT_OTHER_BLUE:
      if ( idx < type1->private_dict.num_other_blues )
      {
        retval = sizeof ( type1->private_dict.other_blues[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.other_blues[idx];
      }
      break;

    case PS_DICT_NUM_FAMILY_BLUES:
      retval = sizeof ( type1->private_dict.num_family_blues );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->private_dict.num_family_blues;
      break;

    case PS_DICT_FAMILY_BLUE:
      if ( idx < type1->private_dict.num_family_blues )
      {
        retval = sizeof ( type1->private_dict.family_blues[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.family_blues[idx];
      }
      break;

    case PS_DICT_NUM_FAMILY_OTHER_BLUES:
      retval = sizeof ( type1->private_dict.num_family_other_blues );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->private_dict.num_family_other_blues;
      break;

    case PS_DICT_FAMILY_OTHER_BLUE:
      if ( idx < type1->private_dict.num_family_other_blues )
      {
        retval = sizeof ( type1->private_dict.family_other_blues[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.family_other_blues[idx];
      }
      break;

    case PS_DICT_NUM_STEM_SNAP_H:
      retval = sizeof ( type1->private_dict.num_snap_widths );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->private_dict.num_snap_widths;
      break;

    case PS_DICT_STEM_SNAP_H:
      if ( idx < type1->private_dict.num_snap_widths )
      {
        retval = sizeof ( type1->private_dict.snap_widths[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.snap_widths[idx];
      }
      break;

    case PS_DICT_NUM_STEM_SNAP_V:
      retval = sizeof ( type1->private_dict.num_snap_heights );
      if ( value && value_len >= retval )
        *((FT_Byte *)value) = type1->private_dict.num_snap_heights;
      break;

    case PS_DICT_STEM_SNAP_V:
      if ( idx < type1->private_dict.num_snap_heights )
      {
        retval = sizeof ( type1->private_dict.snap_heights[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.snap_heights[idx];
      }
      break;

    case PS_DICT_RND_STEM_UP:
      retval = sizeof ( type1->private_dict.round_stem_up );
      if ( value && value_len >= retval )
        *((FT_Bool *)value) = type1->private_dict.round_stem_up;
      break;

    case PS_DICT_FORCE_BOLD:
      retval = sizeof ( type1->private_dict.force_bold );
      if ( value && value_len >= retval )
        *((FT_Bool *)value) = type1->private_dict.force_bold;
      break;

    case PS_DICT_MIN_FEATURE:
      if ( idx < sizeof ( type1->private_dict.min_feature ) /
                   sizeof ( type1->private_dict.min_feature[0] ) )
      {
        retval = sizeof ( type1->private_dict.min_feature[idx] );
        if ( value && value_len >= retval )
          *((FT_Short *)value) = type1->private_dict.min_feature[idx];
      }
      break;

    case PS_DICT_LEN_IV:
      retval = sizeof ( type1->private_dict.lenIV );
      if ( value && value_len >= retval )
        *((FT_Int *)value) = type1->private_dict.lenIV;
      break;

    case PS_DICT_PASSWORD:
      retval = sizeof ( type1->private_dict.password );
      if ( value && value_len >= retval )
        *((FT_Long *)value) = type1->private_dict.password;
      break;

    case PS_DICT_LANGUAGE_GROUP:
      retval = sizeof ( type1->private_dict.language_group );
      if ( value && value_len >= retval )
        *((FT_Long *)value) = type1->private_dict.language_group;
      break;

    case PS_DICT_IS_FIXED_PITCH:
      retval = sizeof ( type1->font_info.is_fixed_pitch );
      if ( value && value_len >= retval )
        *((FT_Bool *)value) = type1->font_info.is_fixed_pitch;
      break;

    case PS_DICT_UNDERLINE_POSITION:
      retval = sizeof ( type1->font_info.underline_position );
      if ( value && value_len >= retval )
        *((FT_Short *)value) = type1->font_info.underline_position;
      break;

    case PS_DICT_UNDERLINE_THICKNESS:
      retval = sizeof ( type1->font_info.underline_thickness );
      if ( value && value_len >= retval )
        *((FT_UShort *)value) = type1->font_info.underline_thickness;
      break;

    case PS_DICT_FS_TYPE:
      retval = sizeof ( type1->font_extra.fs_type );
      if ( value && value_len >= retval )
        *((FT_UShort *)value) = type1->font_extra.fs_type;
      break;

    case PS_DICT_VERSION:
      if ( type1->font_info.version )
      {
        retval = ft_strlen( type1->font_info.version ) + 1;
        if ( value && value_len >= retval )
          ft_memcpy( value, (void *)( type1->font_info.version ), retval );
      }
      break;

    case PS_DICT_NOTICE:
      if ( type1->font_info.notice )
      {
        retval = ft_strlen( type1->font_info.notice ) + 1;
        if ( value && value_len >= retval )
          ft_memcpy( value, (void *)( type1->font_info.notice ), retval );
      }
      break;

    case PS_DICT_FULL_NAME:
      if ( type1->font_info.full_name )
      {
        retval = ft_strlen( type1->font_info.full_name ) + 1;
        if ( value && value_len >= retval )
          ft_memcpy( value, (void *)( type1->font_info.full_name ), retval );
      }
      break;

    case PS_DICT_FAMILY_NAME:
      if ( type1->font_info.family_name )
      {
        retval = ft_strlen( type1->font_info.family_name ) + 1;
        if ( value && value_len >= retval )
          ft_memcpy( value, (void *)( type1->font_info.family_name ),
                     retval );
      }
      break;

    case PS_DICT_WEIGHT:
      if ( type1->font_info.weight )
      {
        retval = ft_strlen( type1->font_info.weight ) + 1;
        if ( value && value_len >= retval )
          ft_memcpy( value, (void *)( type1->font_info.weight ), retval );
      }
      break;

    case PS_DICT_ITALIC_ANGLE:
      retval = sizeof ( type1->font_info.italic_angle );
      if ( value && value_len >= retval )
        *((FT_Long *)value) = type1->font_info.italic_angle;
      break;
    }

    return retval == 0 ? -1 : (FT_Long)retval;
  }

  static const FT_Service_PsInfoRec  t1_service_ps_info =
  {
    (PS_GetFontInfoFunc)   t1_ps_get_font_info,    /* ps_get_font_info    */
    (PS_GetFontExtraFunc)  t1_ps_get_font_extra,   /* ps_get_font_extra   */
    (PS_HasGlyphNamesFunc) t1_ps_has_glyph_names,  /* ps_has_glyph_names  */
    (PS_GetFontPrivateFunc)t1_ps_get_font_private, /* ps_get_font_private */
    (PS_GetFontValueFunc)  t1_ps_get_font_value,   /* ps_get_font_value   */
  };

#ifndef T1_CONFIG_OPTION_NO_AFM
  static const FT_Service_KerningRec  t1_service_kerning =
  {
    T1_Get_Track_Kerning,       /* get_track */
  };
#endif

  /*
   * PROPERTY SERVICE
   *
   */

  FT_DEFINE_SERVICE_PROPERTIESREC(
    t1_service_properties,

    (FT_Properties_SetFunc)ps_property_set,      /* set_property */
    (FT_Properties_GetFunc)ps_property_get )     /* get_property */

  /*
   * SERVICE LIST
   *
   */

  static const FT_ServiceDescRec  t1_services[] =
  {
    { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &t1_service_ps_name },
    { FT_SERVICE_ID_GLYPH_DICT,           &t1_service_glyph_dict },
    { FT_SERVICE_ID_FONT_FORMAT,          FT_FONT_FORMAT_TYPE_1 },
    { FT_SERVICE_ID_POSTSCRIPT_INFO,      &t1_service_ps_info },
    { FT_SERVICE_ID_PROPERTIES,           &t1_service_properties },

#ifndef T1_CONFIG_OPTION_NO_AFM
    { FT_SERVICE_ID_KERNING,              &t1_service_kerning },
#endif

#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
    { FT_SERVICE_ID_MULTI_MASTERS,        &t1_service_multi_masters },
#endif
    { NULL, NULL }
  };

  FT_CALLBACK_DEF( FT_Module_Interface )
  Get_Interface( FT_Module         module,
                 const FT_String*  t1_interface )
  {
    FT_UNUSED( module );

    return ft_service_list_lookup( t1_services, t1_interface );
  }

#ifndef T1_CONFIG_OPTION_NO_AFM

  /**************************************************************************
   *
   * @Function:
   *   Get_Kerning
   *
   * @Description:
   *   A driver method used to return the kerning vector between two
   *   glyphs of the same face.
   *
   * @Input:
   *   face ::
   *     A handle to the source face object.
   *
   *   left_glyph ::
   *     The index of the left glyph in the kern pair.
   *
   *   right_glyph ::
   *     The index of the right glyph in the kern pair.
   *
   * @Output:
   *   kerning ::
   *     The kerning vector.  This is in font units for
   *     scalable formats, and in pixels for fixed-sizes
   *     formats.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   *
   * @Note:
   *   Only horizontal layouts (left-to-right & right-to-left) are
   *   supported by this function.  Other layouts, or more sophisticated
   *   kernings are out of scope of this method (the basic driver
   *   interface is meant to be simple).
   *
   *   They can be implemented by format-specific interfaces.
   */
  static FT_Error
  Get_Kerning( FT_Face     t1face,        /* T1_Face */
               FT_UInt     left_glyph,
               FT_UInt     right_glyph,
               FT_Vector*  kerning )
  {
    T1_Face  face = (T1_Face)t1face;

    kerning->x = 0;
    kerning->y = 0;

    if ( face->afm_data )
      T1_Get_Kerning( (AFM_FontInfo)face->afm_data,
                      left_glyph,
                      right_glyph,
                      kerning );

    return FT_Err_Ok;
  }

#endif /* T1_CONFIG_OPTION_NO_AFM */

  FT_CALLBACK_TABLE_DEF
  const FT_Driver_ClassRec  t1_driver_class =
  {
    {
      FT_MODULE_FONT_DRIVER       |
      FT_MODULE_DRIVER_SCALABLE   |
      FT_MODULE_DRIVER_HAS_HINTER,

      sizeof ( PS_DriverRec ),

      "type1",
      0x10000L,
      0x20000L,

      NULL,    /* module-specific interface */

      T1_Driver_Init,           /* FT_Module_Constructor  module_init   */
      T1_Driver_Done,           /* FT_Module_Destructor   module_done   */
      Get_Interface,            /* FT_Module_Requester    get_interface */
    },

    sizeof ( T1_FaceRec ),
    sizeof ( T1_SizeRec ),
    sizeof ( T1_GlyphSlotRec ),

    T1_Face_Init,               /* FT_Face_InitFunc  init_face */
    T1_Face_Done,               /* FT_Face_DoneFunc  done_face */
    T1_Size_Init,               /* FT_Size_InitFunc  init_size */
    T1_Size_Done,               /* FT_Size_DoneFunc  done_size */
    T1_GlyphSlot_Init,          /* FT_Slot_InitFunc  init_slot */
    T1_GlyphSlot_Done,          /* FT_Slot_DoneFunc  done_slot */

    T1_Load_Glyph,              /* FT_Slot_LoadFunc  load_glyph */

#ifdef T1_CONFIG_OPTION_NO_AFM
    NULL,                       /* FT_Face_GetKerningFunc   get_kerning  */
    NULL,                       /* FT_Face_AttachFunc       attach_file  */
#else
    Get_Kerning,                /* FT_Face_GetKerningFunc   get_kerning  */
    T1_Read_Metrics,            /* FT_Face_AttachFunc       attach_file  */
#endif
    T1_Get_Advances,            /* FT_Face_GetAdvancesFunc  get_advances */

    T1_Size_Request,            /* FT_Size_RequestFunc  request_size */
    NULL                        /* FT_Size_SelectFunc   select_size  */
  };

/* END */


text:
/****************************************************************************
 *
 * t1gload.c
 *
 *   Type 1 Glyph Loader (body).
 *
 * Copyright (C) 1996-2020 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 "t1gload.h"
#include FT_INTERNAL_CALC_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_OUTLINE_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include FT_INTERNAL_CFF_TYPES_H
#include FT_DRIVER_H

#include "t1errors.h"

  /**************************************************************************
   *
   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   * messages during execution.
   */
#undef  FT_COMPONENT
#define FT_COMPONENT  t1gload

  static FT_Error
  T1_Parse_Glyph_And_Get_Char_String( T1_Decoder  decoder,
                                      FT_UInt     glyph_index,
                                      FT_Data*    char_string,
                                      FT_Bool*    force_scaling )
  {
    T1_Face   face  = (T1_Face)decoder->builder.face;
    T1_Font   type1 = &face->type1;
    FT_Error  error = FT_Err_Ok;

    PSAux_Service           psaux         = (PSAux_Service)face->psaux;
    const T1_Decoder_Funcs  decoder_funcs = psaux->t1_decoder_funcs;
    PS_Decoder              psdecoder;

#ifdef FT_CONFIG_OPTION_INCREMENTAL
    FT_Incremental_InterfaceRec *inc =
                      face->root.internal->incremental_interface;
#endif

#ifdef T1_CONFIG_OPTION_OLD_ENGINE
    PS_Driver  driver = (PS_Driver)FT_FACE_DRIVER( face );
#endif

    decoder->font_matrix = type1->font_matrix;
    decoder->font_offset = type1->font_offset;

#ifdef FT_CONFIG_OPTION_INCREMENTAL

    /* For incremental fonts get the character data using the */
    /* callback function.                                     */
    if ( inc )
      error = inc->funcs->get_glyph_data( inc->object,
                                          glyph_index, char_string );
    else

#endif /* FT_CONFIG_OPTION_INCREMENTAL */

    /* For ordinary fonts get the character data stored in the face record. */
    {
      char_string->pointer = type1->charstrings[glyph_index];
      char_string->length  = (FT_Int)type1->charstrings_len[glyph_index];
    }

    if ( !error )
    {
      /* choose which renderer to use */
#ifdef T1_CONFIG_OPTION_OLD_ENGINE
      if ( driver->hinting_engine == FT_HINTING_FREETYPE ||
           decoder->builder.metrics_only                 )
        error = decoder_funcs->parse_charstrings_old(
                  decoder,
                  (FT_Byte*)char_string->pointer,
                  (FT_UInt)char_string->length );
#else
      if ( decoder->builder.metrics_only )
        error = decoder_funcs->parse_metrics(
                  decoder,
                  (FT_Byte*)char_string->pointer,
                  (FT_UInt)char_string->length );
#endif
      else
      {
        CFF_SubFontRec  subfont;

        psaux->ps_decoder_init( &psdecoder, decoder, TRUE );

        psaux->t1_make_subfont( FT_FACE( face ),
                                &face->type1.private_dict, &subfont );
        psdecoder.current_subfont = &subfont;

        error = decoder_funcs->parse_charstrings(
                  &psdecoder,
                  (FT_Byte*)char_string->pointer,
                  (FT_ULong)char_string->length );

        /* Adobe's engine uses 16.16 numbers everywhere;              */
        /* as a consequence, glyphs larger than 2000ppem get rejected */
        if ( FT_ERR_EQ( error, Glyph_Too_Big ) )
        {
          /* this time, we retry unhinted and scale up the glyph later on */
          /* (the engine uses and sets the hardcoded value 0x10000 / 64 = */
          /* 0x400 for both `x_scale' and `y_scale' in this case)         */
          ((T1_GlyphSlot)decoder->builder.glyph)->hint = FALSE;

          *force_scaling = TRUE;

          error = decoder_funcs->parse_charstrings(
                    &psdecoder,
                    (FT_Byte*)char_string->pointer,
                    (FT_ULong)char_string->length );
        }
      }
    }

#ifdef FT_CONFIG_OPTION_INCREMENTAL

    /* Incremental fonts can optionally override the metrics. */
    if ( !error && inc && inc->funcs->get_glyph_metrics )
    {
      FT_Incremental_MetricsRec  metrics;

      metrics.bearing_x = FIXED_TO_INT( decoder->builder.left_bearing.x );
      metrics.bearing_y = 0;
      metrics.advance   = FIXED_TO_INT( decoder->builder.advance.x );
      metrics.advance_v = FIXED_TO_INT( decoder->builder.advance.y );

      error = inc->funcs->get_glyph_metrics( inc->object,
                                             glyph_index, FALSE, &metrics );

      decoder->builder.left_bearing.x = INT_TO_FIXED( metrics.bearing_x );
      decoder->builder.advance.x      = INT_TO_FIXED( metrics.advance );
      decoder->builder.advance.y      = INT_TO_FIXED( metrics.advance_v );
    }

#endif /* FT_CONFIG_OPTION_INCREMENTAL */

    return error;
  }

  FT_CALLBACK_DEF( FT_Error )
  T1_Parse_Glyph( T1_Decoder  decoder,
                  FT_UInt     glyph_index )
  {
    FT_Data   glyph_data;
    FT_Bool   force_scaling = FALSE;
    FT_Error  error         = T1_Parse_Glyph_And_Get_Char_String(
                                decoder, glyph_index, &glyph_data,
                                &force_scaling );

#ifdef FT_CONFIG_OPTION_INCREMENTAL

    if ( !error )
    {
      T1_Face  face = (T1_Face)decoder->builder.face;

      if ( face->root.internal->incremental_interface )
        face->root.internal->incremental_interface->funcs->free_glyph_data(
          face->root.internal->incremental_interface->object,
          &glyph_data );
    }

#endif /* FT_CONFIG_OPTION_INCREMENTAL */

    return error;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /**********                                                      *********/
  /**********            COMPUTE THE MAXIMUM ADVANCE WIDTH         *********/
  /**********                                                      *********/
  /**********    The following code is in charge of computing      *********/
  /**********    the maximum advance width of the font.  It        *********/
  /**********    quickly processes each glyph charstring to        *********/
  /**********    extract the value from either a `sbw' or `seac'   *********/
  /**********    operator.                                         *********/
  /**********                                                      *********/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  FT_LOCAL_DEF( FT_Error )
  T1_Compute_Max_Advance( T1_Face  face,
                          FT_Pos*  max_advance )
  {
    FT_Error       error;
    T1_DecoderRec  decoder;
    FT_Int         glyph_index;
    T1_Font        type1 = &face->type1;
    PSAux_Service  psaux = (PSAux_Service)face->psaux;

    FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) );

    *max_advance = 0;

    /* initialize load decoder */
    error = psaux->t1_decoder_funcs->init( &decoder,
                                           (FT_Face)face,
                                           0, /* size       */
                                           0, /* glyph slot */
                                           (FT_Byte**)type1->glyph_names,
                                           face->blend,
                                           0,
                                           FT_RENDER_MODE_NORMAL,
                                           T1_Parse_Glyph );
    if ( error )
      return error;

    decoder.builder.metrics_only = 1;
    decoder.builder.load_points  = 0;

    decoder.num_subrs     = type1->num_subrs;
    decoder.subrs         = type1->subrs;
    decoder.subrs_len     = type1->subrs_len;
    decoder.subrs_hash    = type1->subrs_hash;

    decoder.buildchar     = face->buildchar;
    decoder.len_buildchar = face->len_buildchar;

    *max_advance = 0;

    FT_TRACE6(( "T1_Compute_Max_Advance:\n" ));

    /* for each glyph, parse the glyph charstring and extract */
    /* the advance width                                      */
    for ( glyph_index = 0; glyph_index < type1->num_glyphs; glyph_index++ )
    {
      /* now get load the unscaled outline */
      (void)T1_Parse_Glyph( &decoder, (FT_UInt)glyph_index );
      if ( glyph_index == 0 || decoder.builder.advance.x > *max_advance )
        *max_advance = decoder.builder.advance.x;

      /* ignore the error if one occurred - skip to next glyph */
    }

    FT_TRACE6(( "T1_Compute_Max_Advance: max advance: %f\n",
                *max_advance / 65536.0 ));

    psaux->t1_decoder_funcs->done( &decoder );

    return FT_Err_Ok;
  }

  FT_LOCAL_DEF( FT_Error )
  T1_Get_Advances( FT_Face    t1face,        /* T1_Face */
                   FT_UInt    first,
                   FT_UInt    count,
                   FT_Int32   load_flags,
                   FT_Fixed*  advances )
  {
    T1_Face        face  = (T1_Face)t1face;
    T1_DecoderRec  decoder;
    T1_Font        type1 = &face->type1;
    PSAux_Service  psaux = (PSAux_Service)face->psaux;
    FT_UInt        nn;
    FT_Error       error;

    FT_TRACE5(( "T1_Get_Advances:\n" ));

    if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
    {
      for ( nn = 0; nn < count; nn++ )
      {
        advances[nn] = 0;

        FT_TRACE5(( "  idx %d: advance height 0 font units\n",
                    first + nn ));
      }

      return FT_Err_Ok;
    }

    error = psaux->t1_decoder_funcs->init( &decoder,
                                           (FT_Face)face,
                                           0, /* size       */
                                           0, /* glyph slot */
                                           (FT_Byte**)type1->glyph_names,
                                           face->blend,
                                           0,
                                           FT_RENDER_MODE_NORMAL,
                                           T1_Parse_Glyph );
    if ( error )
      return error;

    decoder.builder.metrics_only = 1;
    decoder.builder.load_points  = 0;

    decoder.num_subrs  = type1->num_subrs;
    decoder.subrs      = type1->subrs;
    decoder.subrs_len  = type1->subrs_len;
    decoder.subrs_hash = type1->subrs_hash;

    decoder.buildchar     = face->buildchar;
    decoder.len_buildchar = face->len_buildchar;

    for ( nn = 0; nn < count; nn++ )
    {
      error = T1_Parse_Glyph( &decoder, first + nn );
      if ( !error )
        advances[nn] = FIXED_TO_INT( decoder.builder.advance.x );
      else
        advances[nn] = 0;

      FT_TRACE5(( "  idx %d: advance width %d font unit%s\n",
                  first + nn,
                  advances[nn],
                  advances[nn] == 1 ? "" : "s" ));
    }

    return FT_Err_Ok;
  }

  FT_LOCAL_DEF( FT_Error )
  T1_Load_Glyph( FT_GlyphSlot  t1glyph,          /* T1_GlyphSlot */
                 FT_Size       t1size,           /* T1_Size      */
                 FT_UInt       glyph_index,
                 FT_Int32      load_flags )
  {
    T1_GlyphSlot            glyph = (T1_GlyphSlot)t1glyph;
    FT_Error                error;
    T1_DecoderRec           decoder;
    T1_Face                 face = (T1_Face)t1glyph->face;
    FT_Bool                 hinting;
    FT_Bool                 scaled;
    FT_Bool                 force_scaling = FALSE;
    T1_Font                 type1         = &face->type1;
    PSAux_Service           psaux         = (PSAux_Service)face->psaux;
    const T1_Decoder_Funcs  decoder_funcs = psaux->t1_decoder_funcs;

    FT_Matrix               font_matrix;
    FT_Vector               font_offset;
    FT_Data                 glyph_data;
    FT_Bool                 must_finish_decoder = FALSE;
#ifdef FT_CONFIG_OPTION_INCREMENTAL
    FT_Bool                 glyph_data_loaded = 0;
#endif

#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if ( glyph_index >= (FT_UInt)face->root.num_glyphs &&
         !face->root.internal->incremental_interface   )
#else
    if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
#endif /* FT_CONFIG_OPTION_INCREMENTAL */
    {
      error = FT_THROW( Invalid_Argument );
      goto Exit;
    }

    FT_TRACE1(( "T1_Load_Glyph: glyph index %d\n", glyph_index ));

    FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) );

    if ( load_flags & FT_LOAD_NO_RECURSE )
      load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;

    if ( t1size )
    {
      glyph->x_scale = t1size->metrics.x_scale;
      glyph->y_scale = t1size->metrics.y_scale;
    }
    else
    {
      glyph->x_scale = 0x10000L;
      glyph->y_scale = 0x10000L;
    }

    t1glyph->outline.n_points   = 0;
    t1glyph->outline.n_contours = 0;

    hinting = FT_BOOL( !( load_flags & FT_LOAD_NO_SCALE   ) &&
                       !( load_flags & FT_LOAD_NO_HINTING ) );
    scaled  = FT_BOOL( !( load_flags & FT_LOAD_NO_SCALE   ) );

    glyph->hint     = hinting;
    glyph->scaled   = scaled;
    t1glyph->format = FT_GLYPH_FORMAT_OUTLINE;

    error = decoder_funcs->init( &decoder,
                                 t1glyph->face,
                                 t1size,
                                 t1glyph,
                                 (FT_Byte**)type1->glyph_names,
                                 face->blend,
                                 hinting,
                                 FT_LOAD_TARGET_MODE( load_flags ),
                                 T1_Parse_Glyph );
    if ( error )
      goto Exit;

    must_finish_decoder = TRUE;

    decoder.builder.no_recurse = FT_BOOL( load_flags & FT_LOAD_NO_RECURSE );

    decoder.num_subrs     = type1->num_subrs;
    decoder.subrs         = type1->subrs;
    decoder.subrs_len     = type1->subrs_len;
    decoder.subrs_hash    = type1->subrs_hash;

    decoder.buildchar     = face->buildchar;
    decoder.len_buildchar = face->len_buildchar;

    /* now load the unscaled outline */
    error = T1_Parse_Glyph_And_Get_Char_String( &decoder, glyph_index,
                                                &glyph_data,
                                                &force_scaling );
    if ( error )
      goto Exit;
#ifdef FT_CONFIG_OPTION_INCREMENTAL
    glyph_data_loaded = 1;
#endif

    hinting     = glyph->hint;
    font_matrix = decoder.font_matrix;
    font_offset = decoder.font_offset;

    /* save new glyph tables */
    decoder_funcs->done( &decoder );

    must_finish_decoder = FALSE;

    /* now, set the metrics -- this is rather simple, as   */
    /* the left side bearing is the xMin, and the top side */
    /* bearing the yMax                                    */
    if ( !error )
    {
      t1glyph->outline.flags &= FT_OUTLINE_OWNER;
      t1glyph->outline.flags |= FT_OUTLINE_REVERSE_FILL;

      /* for composite glyphs, return only left side bearing and */
      /* advance width                                           */
      if ( load_flags & FT_LOAD_NO_RECURSE )
      {
        FT_Slot_Internal  internal = t1glyph->internal;

        t1glyph->metrics.horiBearingX =
          FIXED_TO_INT( decoder.builder.left_bearing.x );
        t1glyph->metrics.horiAdvance  =
          FIXED_TO_INT( decoder.builder.advance.x );

        internal->glyph_matrix      = font_matrix;
        internal->glyph_delta       = font_offset;
        internal->glyph_transformed = 1;
      }
      else
      {
        FT_BBox            cbox;
        FT_Glyph_Metrics*  metrics = &t1glyph->metrics;

        /* copy the _unscaled_ advance width */
        metrics->horiAdvance =
          FIXED_TO_INT( decoder.builder.advance.x );
        t1glyph->linearHoriAdvance =
          FIXED_TO_INT( decoder.builder.advance.x );
        t1glyph->internal->glyph_transformed = 0;

        if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
        {
          /* make up vertical ones */
          metrics->vertAdvance = ( face->type1.font_bbox.yMax -
                                   face->type1.font_bbox.yMin ) >> 16;
          t1glyph->linearVertAdvance = metrics->vertAdvance;
        }
        else
        {
          metrics->vertAdvance =
            FIXED_TO_INT( decoder.builder.advance.y );
          t1glyph->linearVertAdvance =
            FIXED_TO_INT( decoder.builder.advance.y );
        }

        t1glyph->format = FT_GLYPH_FORMAT_OUTLINE;

        if ( t1size && t1size->metrics.y_ppem < 24 )
          t1glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;

#if 1
        /* apply the font matrix, if any */
        if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L ||
             font_matrix.xy != 0        || font_matrix.yx != 0        )
        {
          FT_Outline_Transform( &t1glyph->outline, &font_matrix );

          metrics->horiAdvance = FT_MulFix( metrics->horiAdvance,
                                            font_matrix.xx );
          metrics->vertAdvance = FT_MulFix( metrics->vertAdvance,
                                            font_matrix.yy );
        }

        if ( font_offset.x || font_offset.y )
        {
          FT_Outline_Translate( &t1glyph->outline,
                                font_offset.x,
                                font_offset.y );

          metrics->horiAdvance += font_offset.x;
          metrics->vertAdvance += font_offset.y;
        }
#endif

        if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || force_scaling )
        {
          /* scale the outline and the metrics */
          FT_Int       n;
          FT_Outline*  cur = decoder.builder.base;
          FT_Vector*   vec = cur->points;
          FT_Fixed     x_scale = glyph->x_scale;
          FT_Fixed     y_scale = glyph->y_scale;

          /* First of all, scale the points, if we are not hinting */
          if ( !hinting || !decoder.builder.hints_funcs )
            for ( n = cur->n_points; n > 0; n--, vec++ )
            {
              vec->x = FT_MulFix( vec->x, x_scale );
              vec->y = FT_MulFix( vec->y, y_scale );
            }

          /* Then scale the metrics */
          metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
          metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
        }

        /* compute the other metrics */
        FT_Outline_Get_CBox( &t1glyph->outline, &cbox );

        metrics->width  = cbox.xMax - cbox.xMin;
        metrics->height = cbox.yMax - cbox.yMin;

        metrics->horiBearingX = cbox.xMin;
        metrics->horiBearingY = cbox.yMax;

        if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
        {
          /* make up vertical ones */
          ft_synthesize_vertical_metrics( metrics,
                                          metrics->vertAdvance );
        }
      }

      /* Set control data to the glyph charstrings.  Note that this is */
      /* _not_ zero-terminated.                                        */
      t1glyph->control_data = (FT_Byte*)glyph_data.pointer;
      t1glyph->control_len  = glyph_data.length;
    }

  Exit:

#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if ( glyph_data_loaded && face->root.internal->incremental_interface )
    {
      face->root.internal->incremental_interface->funcs->free_glyph_data(
        face->root.internal->incremental_interface->object,
        &glyph_data );

      /* Set the control data to null - it is no longer available if   */
      /* loaded incrementally.                                         */
      t1glyph->control_data = NULL;
      t1glyph->control_len  = 0;
    }
#endif

    if ( must_finish_decoder )
      decoder_funcs->done( &decoder );

    return error;
  }

/* END */

repo_language: C

mime_type: text/x-c


text:
/****************************************************************************
 *
 * t1tokens.h
 *
 *   Type 1 tokenizer (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#undef  FT_STRUCTURE
#define FT_STRUCTURE  PS_FontInfoRec
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_FONT_INFO

  T1_FIELD_STRING( "version",            version,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_STRING( "Notice",             notice,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_STRING( "FullName",           full_name,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_STRING( "FamilyName",         family_name,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_STRING( "Weight",             weight,
                   T1_FIELD_DICT_FONTDICT )

  /* we use pointers to detect modifications made by synthetic fonts */
  T1_FIELD_NUM   ( "ItalicAngle",        italic_angle,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_BOOL  ( "isFixedPitch",       is_fixed_pitch,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_NUM   ( "UnderlinePosition",  underline_position,
                   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_NUM   ( "UnderlineThickness", underline_thickness,
                   T1_FIELD_DICT_FONTDICT )

#undef  FT_STRUCTURE
#define FT_STRUCTURE  PS_FontExtraRec
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_FONT_EXTRA

  T1_FIELD_NUM   ( "FSType", fs_type,
                   T1_FIELD_DICT_FONTDICT )

#undef  FT_STRUCTURE
#define FT_STRUCTURE  PS_PrivateRec
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_PRIVATE

  T1_FIELD_NUM       ( "UniqueID",         unique_id,
                       T1_FIELD_DICT_FONTDICT | T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM       ( "lenIV",            lenIV,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM       ( "LanguageGroup",    language_group,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM       ( "password",         password,
                       T1_FIELD_DICT_PRIVATE )

  T1_FIELD_FIXED_1000( "BlueScale",        blue_scale,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM       ( "BlueShift",        blue_shift,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM       ( "BlueFuzz",         blue_fuzz,
                       T1_FIELD_DICT_PRIVATE )

  T1_FIELD_NUM_TABLE ( "BlueValues",       blue_values,        14,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM_TABLE ( "OtherBlues",       other_blues,        10,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM_TABLE ( "FamilyBlues",      family_blues,       14,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM_TABLE ( "FamilyOtherBlues", family_other_blues, 10,
                       T1_FIELD_DICT_PRIVATE )

  T1_FIELD_NUM_TABLE2( "StdHW",            standard_width,      1,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM_TABLE2( "StdVW",            standard_height,     1,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM_TABLE2( "MinFeature",       min_feature,         2,
                       T1_FIELD_DICT_PRIVATE )

  T1_FIELD_NUM_TABLE ( "StemSnapH",        snap_widths,        12,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM_TABLE ( "StemSnapV",        snap_heights,       12,
                       T1_FIELD_DICT_PRIVATE )

  T1_FIELD_FIXED     ( "ExpansionFactor",  expansion_factor,
                       T1_FIELD_DICT_PRIVATE )
  T1_FIELD_BOOL      ( "ForceBold",        force_bold,
                       T1_FIELD_DICT_PRIVATE )

#undef  FT_STRUCTURE
#define FT_STRUCTURE  T1_FontRec
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_FONT_DICT

  T1_FIELD_KEY  ( "FontName",    font_name,    T1_FIELD_DICT_FONTDICT )
  T1_FIELD_NUM  ( "PaintType",   paint_type,   T1_FIELD_DICT_FONTDICT )
  T1_FIELD_NUM  ( "FontType",    font_type,    T1_FIELD_DICT_FONTDICT )
  T1_FIELD_FIXED( "StrokeWidth", stroke_width, T1_FIELD_DICT_FONTDICT )

#undef  FT_STRUCTURE
#define FT_STRUCTURE  FT_BBox
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_BBOX

  T1_FIELD_BBOX( "FontBBox", xMin, T1_FIELD_DICT_FONTDICT )

#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT

#undef  FT_STRUCTURE
#define FT_STRUCTURE  T1_FaceRec
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_FACE

  T1_FIELD_NUM( "NDV", ndv_idx, T1_FIELD_DICT_PRIVATE )
  T1_FIELD_NUM( "CDV", cdv_idx, T1_FIELD_DICT_PRIVATE )

#undef  FT_STRUCTURE
#define FT_STRUCTURE  PS_BlendRec
#undef  T1CODE
#define T1CODE        T1_FIELD_LOCATION_BLEND

  T1_FIELD_NUM_TABLE( "DesignVector", default_design_vector,
                      T1_MAX_MM_DESIGNS, T1_FIELD_DICT_FONTDICT )

#endif /* T1_CONFIG_OPTION_NO_MM_SUPPORT */

/* END */


text:
/****************************************************************************
 *
 * t1afm.h
 *
 *   AFM support for Type 1 fonts (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef T1AFM_H_
#define T1AFM_H_

#include <ft2build.h>
#include "t1objs.h"
#include FT_INTERNAL_TYPE1_TYPES_H

FT_BEGIN_HEADER

  FT_LOCAL( FT_Error )
  T1_Read_Metrics( FT_Face    face,
                   FT_Stream  stream );

  FT_LOCAL( void )
  T1_Done_Metrics( FT_Memory     memory,
                   AFM_FontInfo  fi );

  FT_LOCAL( void )
  T1_Get_Kerning( AFM_FontInfo  fi,
                  FT_UInt       glyph1,
                  FT_UInt       glyph2,
                  FT_Vector*    kerning );

  FT_LOCAL( FT_Error )
  T1_Get_Track_Kerning( FT_Face    face,
                        FT_Fixed   ptsize,
                        FT_Int     degree,
                        FT_Fixed*  kerning );

FT_END_HEADER

#endif /* T1AFM_H_ */

/* END */


text:
/****************************************************************************
 *
 * t1parse.h
 *
 *   Type 1 parser (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef T1PARSE_H_
#define T1PARSE_H_

#include <ft2build.h>
#include FT_INTERNAL_TYPE1_TYPES_H
#include FT_INTERNAL_STREAM_H

FT_BEGIN_HEADER

  /**************************************************************************
   *
   * @Struct:
   *   T1_ParserRec
   *
   * @Description:
   *   A PS_ParserRec is an object used to parse a Type 1 fonts very
   *   quickly.
   *
   * @Fields:
   *   root ::
   *     The root parser.
   *
   *   stream ::
   *     The current input stream.
   *
   *   base_dict ::
   *     A pointer to the top-level dictionary.
   *
   *   base_len ::
   *     The length in bytes of the top dictionary.
   *
   *   private_dict ::
   *     A pointer to the private dictionary.
   *
   *   private_len ::
   *     The length in bytes of the private dictionary.
   *
   *   in_pfb ::
   *     A boolean.  Indicates that we are handling a PFB
   *     file.
   *
   *   in_memory ::
   *     A boolean.  Indicates a memory-based stream.
   *
   *   single_block ::
   *     A boolean.  Indicates that the private dictionary
   *     is stored in lieu of the base dictionary.
   */
  typedef struct  T1_ParserRec_
  {
    PS_ParserRec  root;
    FT_Stream     stream;

    FT_Byte*      base_dict;
    FT_ULong      base_len;

    FT_Byte*      private_dict;
    FT_ULong      private_len;

    FT_Bool       in_pfb;
    FT_Bool       in_memory;
    FT_Bool       single_block;

  } T1_ParserRec, *T1_Parser;

#define T1_Add_Table( p, i, o, l )  (p)->funcs.add( (p), i, o, l )
#define T1_Release_Table( p )          \
          do                           \
          {                            \
            if ( (p)->funcs.release )  \
              (p)->funcs.release( p ); \
          } while ( 0 )

#define T1_Skip_Spaces( p )    (p)->root.funcs.skip_spaces( &(p)->root )
#define T1_Skip_PS_Token( p )  (p)->root.funcs.skip_PS_token( &(p)->root )

#define T1_ToInt( p )       (p)->root.funcs.to_int( &(p)->root )
#define T1_ToFixed( p, t )  (p)->root.funcs.to_fixed( &(p)->root, t )

#define T1_ToCoordArray( p, m, c )                           \
          (p)->root.funcs.to_coord_array( &(p)->root, m, c )
#define T1_ToFixedArray( p, m, f, t )                           \
          (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t )
#define T1_ToToken( p, t )                          \
          (p)->root.funcs.to_token( &(p)->root, t )
#define T1_ToTokenArray( p, t, m, c )                           \
          (p)->root.funcs.to_token_array( &(p)->root, t, m, c )

#define T1_Load_Field( p, f, o, m, pf )                         \
          (p)->root.funcs.load_field( &(p)->root, f, o, m, pf )

#define T1_Load_Field_Table( p, f, o, m, pf )                         \
          (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf )

  FT_LOCAL( FT_Error )
  T1_New_Parser( T1_Parser      parser,
                 FT_Stream      stream,
                 FT_Memory      memory,
                 PSAux_Service  psaux );

  FT_LOCAL( FT_Error )
  T1_Get_Private_Dict( T1_Parser      parser,
                       PSAux_Service  psaux );

  FT_LOCAL( void )
  T1_Finalize_Parser( T1_Parser  parser );

FT_END_HEADER

#endif /* T1PARSE_H_ */

/* END */


text:
/****************************************************************************
 *
 * t1driver.h
 *
 *   High-level Type 1 driver interface (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef T1DRIVER_H_
#define T1DRIVER_H_

#include <ft2build.h>
#include FT_INTERNAL_DRIVER_H

FT_BEGIN_HEADER

  FT_EXPORT_VAR( const FT_Driver_ClassRec )  t1_driver_class;

FT_END_HEADER

#endif /* T1DRIVER_H_ */

/* END */


text:
# FreeType 2 src/type1 Jamfile
#
# Copyright (C) 2001-2020 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.

SubDir  FT2_TOP $(FT2_SRC_DIR) type1 ;

{
  local  _sources ;

  if $(FT2_MULTI)
  {
    _sources = t1afm
               t1driver
               t1gload
               t1load
               t1objs
               t1parse
               ;
  }
  else
  {
    _sources = type1 ;
  }

  Library  $(FT2_LIB) : $(_sources).c ;
}

# end of src/type1 Jamfile


text:
/****************************************************************************
 *
 * t1errors.h
 *
 *   Type 1 error codes (specification only).
 *
 * Copyright (C) 2001-2020 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.
 *
 */

  /**************************************************************************
   *
   * This file is used to define the Type 1 error enumeration constants.
   *
   */

#ifndef T1ERRORS_H_
#define T1ERRORS_H_

#include FT_MODULE_ERRORS_H

#undef FTERRORS_H_

#undef  FT_ERR_PREFIX
#define FT_ERR_PREFIX  T1_Err_
#define FT_ERR_BASE    FT_Mod_Err_Type1

#include FT_ERRORS_H

#endif /* T1ERRORS_H_ */

/* END */


text:
#
# FreeType 2 Type1 module definition
#

# Copyright (C) 1996-2020 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.

FTMODULE_H_COMMANDS += TYPE1_DRIVER

define TYPE1_DRIVER
$(OPEN_DRIVER) FT_Driver_ClassRec, t1_driver_class $(CLOSE_DRIVER)
$(ECHO_DRIVER)type1     $(ECHO_DRIVER_DESC)Postscript font files with extension *.pfa or *.pfb$(ECHO_DRIVER_DONE)
endef

# EOF


text:
/****************************************************************************
 *
 * t1gload.h
 *
 *   Type 1 Glyph Loader (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef T1GLOAD_H_
#define T1GLOAD_H_

#include <ft2build.h>
#include "t1objs.h"

FT_BEGIN_HEADER

  FT_LOCAL( FT_Error )
  T1_Compute_Max_Advance( T1_Face  face,
                          FT_Pos*  max_advance );

  FT_LOCAL( FT_Error )
  T1_Get_Advances( FT_Face    face,
                   FT_UInt    first,
                   FT_UInt    count,
                   FT_Int32   load_flags,
                   FT_Fixed*  advances );

  FT_LOCAL( FT_Error )
  T1_Load_Glyph( FT_GlyphSlot  glyph,
                 FT_Size       size,
                 FT_UInt       glyph_index,
                 FT_Int32      load_flags );

FT_END_HEADER

#endif /* T1GLOAD_H_ */

/* END */


text:
/****************************************************************************
 *
 * type1.c
 *
 *   FreeType Type 1 driver component (body only).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#define FT_MAKE_OPTION_SINGLE_OBJECT
#include <ft2build.h>

#include "t1afm.c"
#include "t1driver.c"
#include "t1gload.c"
#include "t1load.c"
#include "t1objs.c"
#include "t1parse.c"

/* END */


text:
/****************************************************************************
 *
 * t1load.h
 *
 *   Type 1 font loader (specification).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

#ifndef T1LOAD_H_
#define T1LOAD_H_

#include <ft2build.h>
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include FT_MULTIPLE_MASTERS_H

#include "t1parse.h"

FT_BEGIN_HEADER

  typedef struct  T1_Loader_
  {
    T1_ParserRec  parser;          /* parser used to read the stream */

    FT_Int        num_chars;       /* number of characters in encoding */
    PS_TableRec   encoding_table;  /* PS_Table used to store the       */
                                   /* encoding character names         */

    FT_Int        num_glyphs;
    PS_TableRec   glyph_names;
    PS_TableRec   charstrings;
    PS_TableRec   swap_table;      /* For moving .notdef glyph to index 0. */

    FT_Int        num_subrs;
    PS_TableRec   subrs;
    FT_Hash       subrs_hash;
    FT_Bool       fontdata;

    FT_UInt       keywords_encountered; /* T1_LOADER_ENCOUNTERED_XXX */

  } T1_LoaderRec, *T1_Loader;

  /* treatment of some keywords differs depending on whether */
  /* they precede or follow certain other keywords           */

#define T1_PRIVATE                ( 1 << 0 )
#define T1_FONTDIR_AFTER_PRIVATE  ( 1 << 1 )

  FT_LOCAL( FT_Error )
  T1_Open_Face( T1_Face  face );

#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT

  FT_LOCAL( FT_Error )
  T1_Get_Multi_Master( T1_Face           face,
                       FT_Multi_Master*  master );

  FT_LOCAL( FT_Error )
  T1_Get_MM_Var( T1_Face      face,
                 FT_MM_Var*  *master );

  FT_LOCAL( FT_Error )
  T1_Set_MM_Blend( T1_Face    face,
                   FT_UInt    num_coords,
                   FT_Fixed*  coords );

  FT_LOCAL( FT_Error )
  T1_Get_MM_Blend( T1_Face    face,
                   FT_UInt    num_coords,
                   FT_Fixed*  coords );

  FT_LOCAL( FT_Error )
  T1_Set_MM_Design( T1_Face   face,
                    FT_UInt   num_coords,
                    FT_Long*  coords );

  FT_LOCAL( FT_Error )
  T1_Reset_MM_Blend( T1_Face  face,
                     FT_UInt  instance_index );

  FT_LOCAL( FT_Error )
  T1_Get_Var_Design( T1_Face    face,
                     FT_UInt    num_coords,
                     FT_Fixed*  coords );

  FT_LOCAL( FT_Error )
  T1_Set_Var_Design( T1_Face    face,
                     FT_UInt    num_coords,
                     FT_Fixed*  coords );

  FT_LOCAL( void )
  T1_Done_Blend( T1_Face  face );

  FT_LOCAL( FT_Error )
  T1_Set_MM_WeightVector( T1_Face    face,
                          FT_UInt    len,
                          FT_Fixed*  weightvector );

  FT_LOCAL( FT_Error )
  T1_Get_MM_WeightVector( T1_Face    face,
                          FT_UInt*   len,
                          FT_Fixed*  weightvector );

#endif /* !T1_CONFIG_OPTION_NO_MM_SUPPORT */

FT_END_HEADER

#endif /* T1LOAD_H_ */

/* END */


text:
#
# FreeType 2 Type1 driver configuration rules
#

# Copyright (C) 1996-2020 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.

# Type1 driver directory
#
T1_DIR := $(SRC_DIR)/type1

# compilation flags for the driver
#
T1_COMPILE := $(CC) $(ANSIFLAGS)                           \
                    $I$(subst /,$(COMPILER_SEP),$(T1_DIR)) \
                    $(INCLUDE_FLAGS)                       \
                    $(FT_CFLAGS)

# Type1 driver sources (i.e., C files)
#
T1_DRV_SRC := $(T1_DIR)/t1parse.c  \
              $(T1_DIR)/t1load.c   \
              $(T1_DIR)/t1driver.c \
              $(T1_DIR)/t1afm.c    \
              $(T1_DIR)/t1gload.c  \
              $(T1_DIR)/t1objs.c

# Type1 driver headers
#
T1_DRV_H := $(T1_DRV_SRC:%.c=%.h) \
            $(T1_DIR)/t1tokens.h  \
            $(T1_DIR)/t1errors.h

# Type1 driver object(s)
#
#   T1_DRV_OBJ_M is used during `multi' builds
#   T1_DRV_OBJ_S is used during `single' builds
#
T1_DRV_OBJ_M := $(T1_DRV_SRC:$(T1_DIR)/%.c=$(OBJ_DIR)/%.$O)
T1_DRV_OBJ_S := $(OBJ_DIR)/type1.$O

# Type1 driver source file for single build
#
T1_DRV_SRC_S := $(T1_DIR)/type1.c

# Type1 driver - single object
#
$(T1_DRV_OBJ_S): $(T1_DRV_SRC_S) $(T1_DRV_SRC) $(FREETYPE_H) $(T1_DRV_H)
	$(T1_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(T1_DRV_SRC_S))

# Type1 driver - multiple objects
#
$(OBJ_DIR)/%.$O: $(T1_DIR)/%.c $(FREETYPE_H) $(T1_DRV_H)
	$(T1_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)

# update main driver object lists
#
DRV_OBJS_S += $(T1_DRV_OBJ_S)
DRV_OBJS_M += $(T1_DRV_OBJ_M)

# EOF


text:
/****************************************************************************
 *
 * t1parse.c
 *
 *   Type 1 parser (body).
 *
 * Copyright (C) 1996-2020 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.
 *
 */

  /**************************************************************************
   *
   * The Type 1 parser is in charge of the following:
   *
   * - provide an implementation of a growing sequence of objects called
   *   a `T1_Table' (used to build various tables needed by the loader).
   *
   * - opening .pfb and .pfa files to extract their top-level and private
   *   dictionaries.
   *
   * - read numbers, arrays & strings from any dictionary.
   *
   * See `t1load.c' to see how data is loaded from the font file.
   *
   */

#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H

#include "t1parse.h"

#include "t1errors.h"

  /**************************************************************************
   *
   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   * messages during execution.
   */
#undef  FT_COMPONENT
#define FT_COMPONENT  t1parse

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                   INPUT STREAM PARSER                         *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  /* see Adobe Technical Note 5040.Download_Fonts.pdf */

  static FT_Error
  read_pfb_tag( FT_Stream   stream,
                FT_UShort  *atag,
                FT_ULong   *asize )
  {
    FT_Error   error;
    FT_UShort  tag;
    FT_ULong   size;

    *atag  = 0;
    *asize = 0;

    if ( !FT_READ_USHORT( tag ) )
    {
      if ( tag == 0x8001U || tag == 0x8002U )
      {
        if ( !FT_READ_ULONG_LE( size ) )
          *asize = size;
      }

      *atag = tag;
    }

    return error;
  }

  static FT_Error
  check_type1_format( FT_Stream    stream,
                      const char*  header_string,
                      size_t       header_length )
  {
    FT_Error   error;
    FT_UShort  tag;
    FT_ULong   dummy;

    if ( FT_STREAM_SEEK( 0 ) )
      goto Exit;

    error = read_pfb_tag( stream, &tag, &dummy );
    if ( error )
      goto Exit;

    /* We assume that the first segment in a PFB is always encoded as   */
    /* text.  This might be wrong (and the specification doesn't insist */
    /* on that), but we have never seen a counterexample.               */
    if ( tag != 0x8001U && FT_STREAM_SEEK( 0 ) )
      goto Exit;

    if ( !FT_FRAME_ENTER( header_length ) )
    {
      error = FT_Err_Ok;

      if ( ft_memcmp( stream->cursor, header_string, header_length ) != 0 )
        error = FT_THROW( Unknown_File_Format );

      FT_FRAME_EXIT();
    }

  Exit:
    return error;
  }

  FT_LOCAL_DEF( FT_Error )
  T1_New_Parser( T1_Parser      parser,
                 FT_Stream      stream,
                 FT_Memory      memory,
                 PSAux_Service  psaux )
  {
    FT_Error   error;
    FT_UShort  tag;
    FT_ULong   size;

    psaux->ps_parser_funcs->init( &parser->root, NULL, NULL, memory );

    parser->stream       = stream;
    parser->base_len     = 0;
    parser->base_dict    = NULL;
    parser->private_len  = 0;
    parser->private_dict = NULL;
    parser->in_pfb       = 0;
    parser->in_memory    = 0;
    parser->single_block = 0;

    /* check the header format */
    error = check_type1_format( stream, "%!PS-AdobeFont", 14 );
    if ( error )
    {
      if ( FT_ERR_NEQ( error, Unknown_File_Format ) )
        goto Exit;

      error = check_type1_format( stream, "%!FontType", 10 );
      if ( error )
      {
        FT_TRACE2(( "  not a Type 1 font\n" ));
        goto Exit;
      }
    }

    /*******************************************************************
     *
     * Here a short summary of what is going on:
     *
     *   When creating a new Type 1 parser, we try to locate and load
     *   the base dictionary if this is possible (i.e., for PFB
     *   files).  Otherwise, we load the whole font into memory.
     *
     *   When `loading' the base dictionary, we only setup pointers
     *   in the case of a memory-based stream.  Otherwise, we
     *   allocate and load the base dictionary in it.
     *
     *   parser->in_pfb is set if we are in a binary (`.pfb') font.
     *   parser->in_memory is set if we have a memory stream.
     */

    /* try to compute the size of the base dictionary;     */
    /* look for a Postscript binary file tag, i.e., 0x8001 */
    if ( FT_STREAM_SEEK( 0L ) )
      goto Exit;

    error = read_pfb_tag( stream, &tag, &size );
    if ( error )
      goto Exit;

    if ( tag != 0x8001U )
    {
      /* assume that this is a PFA file for now; an error will */
      /* be produced later when more things are checked        */
      if ( FT_STREAM_SEEK( 0L ) )
        goto Exit;
      size = stream->size;
    }
    else
      parser->in_pfb = 1;

    /* now, try to load `size' bytes of the `base' dictionary we */
    /* found previously                                          */

    /* if it is a memory-based resource, set up pointers */
    if ( !stream->read )
    {
      parser->base_dict = (FT_Byte*)stream->base + stream->pos;
      parser->base_len  = size;
      parser->in_memory = 1;

      /* check that the `size' field is valid */
      if ( FT_STREAM_SKIP( size ) )
        goto Exit;
    }
    else
    {
      /* read segment in memory -- this is clumsy, but so does the format */
      if ( FT_ALLOC( parser->base_dict, size )       ||
           FT_STREAM_READ( parser->base_dict, size ) )
        goto Exit;
      parser->base_len = size;
    }

    parser->root.base   = parser->base_dict;
    parser->root.cursor = parser->base_dict;
    parser->root.limit  = parser->root.cursor + parser->base_len;

  Exit:
    if ( error && !parser->in_memory )
      FT_FREE( parser->base_dict );

    return error;
  }

  FT_LOCAL_DEF( void )
  T1_Finalize_Parser( T1_Parser  parser )
  {
    FT_Memory  memory = parser->root.memory;

    /* always free the private dictionary */
    FT_FREE( parser->private_dict );

    /* free the base dictionary only when we have a disk stream */
    if ( !parser->in_memory )
      FT_FREE( parser->base_dict );

    parser->root.funcs.done( &parser->root );
  }

  FT_LOCAL_DEF( FT_Error )
  T1_Get_Private_Dict( T1_Parser      parser,
                       PSAux_Service  psaux )
  {
    FT_Stream  stream = parser->stream;
    FT_Memory  memory = parser->root.memory;
    FT_Error   error  = FT_Err_Ok;
    FT_ULong   size;

    if ( parser->in_pfb )
    {
      /* in the case of the PFB format, the private dictionary can be  */
      /* made of several segments.  We thus first read the number of   */
      /* segments to compute the total size of the private dictionary  */
      /* then re-read them into memory.                                */
      FT_ULong   start_pos = FT_STREAM_POS();
      FT_UShort  tag;

      parser->private_len = 0;
      for (;;)
      {
        error = read_pfb_tag( stream, &tag, &size );
        if ( error )
          goto Fail;

        if ( tag != 0x8002U )
          break;

        parser->private_len += size;

        if ( FT_STREAM_SKIP( size ) )
          goto Fail;
      }

      /* Check that we have a private dictionary there */
      /* and allocate private dictionary buffer        */
      if ( parser->private_len == 0 )
      {
        FT_ERROR(( "T1_Get_Private_Dict:"
                   " invalid private dictionary section\n" ));
        error = FT_THROW( Invalid_File_Format );
        goto Fail;
      }

      if ( FT_STREAM_SEEK( start_pos )                           ||
           FT_ALLOC( parser->private_dict, parser->private_len ) )
        goto Fail;

      parser->private_len = 0;
      for (;;)
      {
        error = read_pfb_tag( stream, &tag, &size );
        if ( error || tag != 0x8002U )
        {
          error = FT_Err_Ok;
          break;
        }

        if ( FT_STREAM_READ( parser->private_dict + parser->private_len,
                             size ) )
          goto Fail;

        parser->private_len += size;
      }
    }
    else
    {
      /* We have already `loaded' the whole PFA font file into memory; */
      /* if this is a memory resource, allocate a new block to hold    */
      /* the private dict.  Otherwise, simply overwrite into the base  */
      /* dictionary block in the heap.                                 */

      /* first of all, look at the `eexec' keyword */
      FT_Byte*    cur   = parser->base_dict;
      FT_Byte*    limit = cur + parser->base_len;
      FT_Pointer  pos_lf;
      FT_Bool     test_cr;

    Again:
      for (;;)
      {
        if ( cur[0] == 'e'   &&
             cur + 9 < limit )      /* 9 = 5 letters for `eexec' + */
                                    /* whitespace + 4 chars        */
        {
          if ( cur[1] == 'e' &&
               cur[2] == 'x' &&
               cur[3] == 'e' &&
               cur[4] == 'c' )
            break;
        }
        cur++;
        if ( cur >= limit )
        {
          FT_ERROR(( "T1_Get_Private_Dict:"
                     " could not find `eexec' keyword\n" ));
          error = FT_THROW( Invalid_File_Format );
          goto Exit;
        }
      }

      /* check whether `eexec' was real -- it could be in a comment */
      /* or string (as e.g. in u003043t.gsf from ghostscript)       */

      parser->root.cursor = parser->base_dict;
      /* set limit to `eexec' + whitespace + 4 characters */
      parser->root.limit  = cur + 10;

      cur   = parser->root.cursor;
      limit = parser->root.limit;

      while ( cur < limit )
      {
        if ( cur[0] == 'e'   &&
             cur + 5 < limit )
        {
          if ( cur[1] == 'e' &&
               cur[2] == 'x' &&
               cur[3] == 'e' &&
               cur[4] == 'c' )
            goto Found;
        }

        T1_Skip_PS_Token( parser );
        if ( parser->root.error )
          break;
        T1_Skip_Spaces  ( parser );
        cur = parser->root.cursor;
      }

      /* we haven't found the correct `eexec'; go back and continue */
      /* searching                                                  */

      cur   = limit;
      limit = parser->base_dict + parser->base_len;

      if ( cur >= limit )
      {
        FT_ERROR(( "T1_Get_Private_Dict:"
                   " premature end in private dictionary\n" ));
        error = FT_THROW( Invalid_File_Format );
        goto Exit;
      }

      goto Again;

      /* now determine where to write the _encrypted_ binary private  */
      /* dictionary.  We overwrite the base dictionary for disk-based */
      /* resources and allocate a new block otherwise                 */

    Found:
      parser->root.limit = parser->base_dict + parser->base_len;

      T1_Skip_PS_Token( parser );
      cur   = parser->root.cursor;
      limit = parser->root.limit;

      /* According to the Type 1 spec, the first cipher byte must not be */
      /* an ASCII whitespace character code (blank, tab, carriage return */
      /* or line feed).  We have seen Type 1 fonts with two line feed    */
      /* characters...  So skip now all whitespace character codes.      */
      /*                                                                 */
      /* On the other hand, Adobe's Type 1 parser handles fonts just     */
      /* fine that are violating this limitation, so we add a heuristic  */
      /* test to stop at \r only if it is not used for EOL.              */

      pos_lf  = ft_memchr( cur, '\n', (size_t)( limit - cur ) );
      test_cr = FT_BOOL( !pos_lf                                       ||
                         pos_lf > ft_memchr( cur,
                                             '\r',
                                             (size_t)( limit - cur ) ) );

      while ( cur < limit                    &&
              ( *cur == ' '                ||
                *cur == '\t'               ||
                (test_cr && *cur == '\r' ) ||
                *cur == '\n'               ) )
        cur++;
      if ( cur >= limit )
      {
        FT_ERROR(( "T1_Get_Private_Dict:"
                   " `eexec' not properly terminated\n" ));
        error = FT_THROW( Invalid_File_Format );
        goto Exit;
      }

      size = parser->base_len - (FT_ULong)( cur - parser->base_dict );

      if ( parser->in_memory )
      {
        /* note that we allocate one more byte to put a terminating `0' */
        if ( FT_ALLOC( parser->private_dict, size + 1 ) )
          goto Fail;
        parser->private_len = size;
      }
      else
      {
        parser->single_block = 1;
        parser->private_dict = parser->base_dict;
        parser->private_len  = size;
        parser->base_dict    = NULL;
        parser->base_len     = 0;
      }

      /* now determine whether the private dictionary is encoded in binary */
      /* or hexadecimal ASCII format -- decode it accordingly              */

      /* we need to access the next 4 bytes (after the final whitespace */
      /* following the `eexec' keyword); if they all are hexadecimal    */
      /* digits, then we have a case of ASCII storage                   */

      if ( cur + 3 < limit                                &&
           ft_isxdigit( cur[0] ) && ft_isxdigit( cur[1] ) &&
           ft_isxdigit( cur[2] ) && ft_isxdigit( cur[3] ) )
      {
        /* ASCII hexadecimal encoding */
        FT_ULong  len;

        parser->root.cursor = cur;
        (void)psaux->ps_parser_funcs->to_bytes( &parser->root,
                                                parser->private_dict,
                                                parser->private_len,
                                                &len,
                                                0 );
        parser->private_len = len;

        /* put a safeguard */
        parser->private_dict[len] = '\0';
      }
      else
        /* binary encoding -- copy the private dict */
        FT_MEM_MOVE( parser->private_dict, cur, size );
    }

    /* we now decrypt the encoded binary private dictionary */
    psaux->t1_decrypt( parser->private_dict, parser->private_len, 55665U );

    if ( parser->private_len < 4 )
    {
      FT_ERROR(( "T1_Get_Private_Dict:"
                 " invalid private dictionary section\n" ));
      error = FT_THROW( Invalid_File_Format );
      goto Fail;
    }

    /* replace the four random bytes at the beginning with whitespace */
    parser->private_dict[0] = ' ';
    parser->private_dict[1] = ' ';
    parser->private_dict[2] = ' ';
    parser->private_dict[3] = ' ';

    parser->root.base   = parser->private_dict;
    parser->root.cursor = parser->private_dict;
    parser->root.limit  = parser->root.cursor + parser->private_len;

  Fail:
  Exit:
    return error;
  }

/* END */


text:
/****************************************************************************
 *
 * t1afm.c
 *
 *   AFM support for Type 1 fonts (body).
 *
 * Copyright (C) 1996-2020 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 "t1afm.h"
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include "t1errors.h"

#ifndef T1_CONFIG_OPTION_NO_AFM

  /**************************************************************************
   *
   * 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  t1afm

  FT_LOCAL_DEF( void )
  T1_Done_Metrics( FT_Memory     memory,
                   AFM_FontInfo  fi )
  {
    FT_FREE( fi->KernPairs );
    fi->NumKernPair = 0;

    FT_FREE( fi->TrackKerns );
    fi->NumTrackKern = 0;

    FT_FREE( fi );
  }

  /* read a glyph name and return the equivalent glyph index */
  static FT_Int
  t1_get_index( const char*  name,
                FT_Offset    len,
                void*        user_data )
  {
    T1_Font  type1 = (T1_Font)user_data;
    FT_Int   n;

    /* PS string/name length must be < 16-bit */
    if ( len > 0xFFFFU )
      return 0;

    for ( n = 0; n < type1->num_glyphs; n++ )
    {
      char*  gname = (char*)type1->glyph_names[n];

      if ( gname && gname[0] == name[0]        &&
           ft_strlen( gname ) == len           &&
           ft_strncmp( gname, name, len ) == 0 )
        return n;
    }

    return 0;
  }

#undef  KERN_INDEX
#define KERN_INDEX( g1, g2 )  ( ( (FT_ULong)(g1) << 16 ) | (g2) )

  /* compare two kerning pairs */
  FT_CALLBACK_DEF( int )
  compare_kern_pairs( const void*  a,
                      const void*  b )
  {
    AFM_KernPair  pair1 = (AFM_KernPair)a;
    AFM_KernPair  pair2 = (AFM_KernPair)b;

    FT_ULong  index1 = KERN_INDEX( pair1->index1, pair1->index2 );
    FT_ULong  index2 = KERN_INDEX( pair2->index1, pair2->index2 );

    if ( index1 > index2 )
      return 1;
    else if ( index1 < index2 )
      return -1;
    else
      return 0;
  }

  /* parse a PFM file -- for now, only read the kerning pairs */
  static FT_Error
  T1_Read_PFM( FT_Face       t1_face,
               FT_Stream     stream,
               AFM_FontInfo  fi )
  {
    FT_Error      error  = FT_Err_Ok;
    FT_Memory     memory = stream->memory;
    FT_Byte*      start;
    FT_Byte*      limit;
    FT_Byte*      p;
    AFM_KernPair  kp;
    FT_Int        width_table_length;
    FT_CharMap    oldcharmap;
    FT_CharMap    charmap;
    FT_Int        n;

    start = (FT_Byte*)stream->cursor;
    limit = (FT_Byte*)stream->limit;

    /* Figure out how long the width table is.          */
    /* This info is a little-endian short at offset 99. */
    p = start + 99;
    if ( p + 2 > limit )
    {
      error = FT_THROW( Unknown_File_Format );
      goto Exit;
    }
    width_table_length = FT_PEEK_USHORT_LE( p );

    p += 18 + width_table_length;
    if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )
      /* extension table is probably optional */
      goto Exit;

    /* Kerning offset is 14 bytes from start of extensions table. */
    p += 14;
    p = start + FT_PEEK_ULONG_LE( p );

    if ( p == start )
      /* zero offset means no table */
      goto Exit;

    if ( p + 2 > limit )
    {
      error = FT_THROW( Unknown_File_Format );
      goto Exit;
    }

    fi->NumKernPair = FT_PEEK_USHORT_LE( p );
    p += 2;
    if ( p + 4 * fi->NumKernPair > limit )
    {
      error = FT_THROW( Unknown_File_Format );
      goto Exit;
    }

    /* Actually, kerning pairs are simply optional! */
    if ( fi->NumKernPair == 0 )
      goto Exit;

    /* allocate the pairs */
    if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
      goto Exit;

    /* now, read each kern pair */
    kp    = fi->KernPairs;
    limit = p + 4 * fi->NumKernPair;

    /* PFM kerning data are stored by encoding rather than glyph index, */
    /* so find the PostScript charmap of this font and install it       */
    /* temporarily.  If we find no PostScript charmap, then just use    */
    /* the default and hope it is the right one.                        */
    oldcharmap = t1_face->charmap;
    charmap    = NULL;

    for ( n = 0; n < t1_face->num_charmaps; n++ )
    {
      charmap = t1_face->charmaps[n];
      /* check against PostScript pseudo platform */
      if ( charmap->platform_id == 7 )
      {
        error = FT_Set_Charmap( t1_face, charmap );
        if ( error )
          goto Exit;
        break;
      }
    }

    /* Kerning info is stored as:             */
    /*                                        */
    /*   encoding of first glyph (1 byte)     */
    /*   encoding of second glyph (1 byte)    */
    /*   offset (little-endian short)         */
    for ( ; p < limit; p += 4 )
    {
      kp->index1 = FT_Get_Char_Index( t1_face, p[0] );
      kp->index2 = FT_Get_Char_Index( t1_face, p[1] );

      kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2);
      kp->y = 0;

      kp++;
    }

    if ( oldcharmap )
      error = FT_Set_Charmap( t1_face, oldcharmap );
    if ( error )
      goto Exit;

    /* now, sort the kern pairs according to their glyph indices */
    ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),
              compare_kern_pairs );

  Exit:
    if ( error )
    {
      FT_FREE( fi->KernPairs );
      fi->NumKernPair = 0;
    }

    return error;
  }

  /* parse a metrics file -- either AFM or PFM depending on what */
  /* it turns out to be                                          */
  FT_LOCAL_DEF( FT_Error )
  T1_Read_Metrics( FT_Face    t1_face,
                   FT_Stream  stream )
  {
    PSAux_Service  psaux;
    FT_Memory      memory  = stream->memory;
    AFM_ParserRec  parser;
    AFM_FontInfo   fi      = NULL;
    FT_Error       error   = FT_ERR( Unknown_File_Format );
    T1_Face        face    = (T1_Face)t1_face;
    T1_Font        t1_font = &face->type1;

    if ( face->afm_data )
    {
      FT_TRACE1(( "T1_Read_Metrics:"
                  " Freeing previously attached metrics data.\n" ));
      T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data );

      face->afm_data = NULL;
    }

    if ( FT_NEW( fi )                   ||
         FT_FRAME_ENTER( stream->size ) )
      goto Exit;

    fi->FontBBox  = t1_font->font_bbox;
    fi->Ascender  = t1_font->font_bbox.yMax;
    fi->Descender = t1_font->font_bbox.yMin;

    psaux = (PSAux_Service)face->psaux;
    if ( psaux->afm_parser_funcs )
    {
      error = psaux->afm_parser_funcs->init( &parser,
                                             stream->memory,
                                             stream->cursor,
                                             stream->limit );

      if ( !error )
      {
        parser.FontInfo  = fi;
        parser.get_index = t1_get_index;
        parser.user_data = t1_font;

        error = psaux->afm_parser_funcs->parse( &parser );
        psaux->afm_parser_funcs->done( &parser );
      }
    }

    if ( FT_ERR_EQ( error, Unknown_File_Format ) )
    {
      FT_Byte*  start = stream->cursor;

      /* MS Windows allows versions up to 0x3FF without complaining */
      if ( stream->size > 6                              &&
           start[1] < 4                                  &&
           FT_PEEK_ULONG_LE( start + 2 ) == stream->size )
        error = T1_Read_PFM( t1_face, stream, fi );
    }

    if ( !error )
    {
      t1_font->font_bbox = fi->FontBBox;

      t1_face->bbox.xMin =   fi->FontBBox.xMin            >> 16;
      t1_face->bbox.yMin =   fi->FontBBox.yMin            >> 16;
      /* no `U' suffix here to 0xFFFF! */
      t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16;
      t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16;

      /* no `U' suffix here to 0x8000! */
      t1_face->ascender  = (FT_Short)( ( fi->Ascender  + 0x8000 ) >> 16 );
      t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 );

      if ( fi->NumKernPair )
      {
        t1_face->face_flags |= FT_FACE_FLAG_KERNING;
        face->afm_data       = fi;
        fi                   = NULL;
      }
    }

    FT_FRAME_EXIT();

  Exit:
    if ( fi )
      T1_Done_Metrics( memory, fi );

    return error;
  }

  /* find the kerning for a given glyph pair */
  FT_LOCAL_DEF( void )
  T1_Get_Kerning( AFM_FontInfo  fi,
                  FT_UInt       glyph1,
                  FT_UInt       glyph2,
                  FT_Vector*    kerning )
  {
    AFM_KernPair  min, mid, max;
    FT_ULong      idx = KERN_INDEX( glyph1, glyph2 );

    /* simple binary search */
    min = fi->KernPairs;
    max = min + fi->NumKernPair - 1;

    while ( min <= max )
    {
      FT_ULong  midi;

      mid  = min + ( max - min ) / 2;
      midi = KERN_INDEX( mid->index1, mid->index2 );

      if ( midi == idx )
      {
        kerning->x = mid->x;
        kerning->y = mid->y;

        return;
      }

      if ( midi < idx )
        min = mid + 1;
      else
        max = mid - 1;
    }

    kerning->x = 0;
    kerning->y = 0;
  }

  FT_LOCAL_DEF( FT_Error )
  T1_Get_Track_Kerning( FT_Face    face,
                        FT_Fixed   ptsize,
                        FT_Int     degree,
                        FT_Fixed*  kerning )
  {
    AFM_FontInfo  fi = (AFM_FontInfo)( (T1_Face)face )->afm_data;
    FT_UInt       i;

    if ( !fi )
      return FT_THROW( Invalid_Argument );

    for ( i = 0; i < fi->NumTrackKern; i++ )
    {
      AFM_TrackKern  tk = fi->TrackKerns + i;

      if ( tk->degree != degree )
        continue;

      if ( ptsize < tk->min_ptsize )
        *kerning = tk->min_kern;
      else if ( ptsize > tk->max_ptsize )
        *kerning = tk->max_kern;
      else
      {
        *kerning = FT_MulDiv( ptsize - tk->min_ptsize,
                              tk->max_kern - tk->min_kern,
                              tk->max_ptsize - tk->min_ptsize ) +
                   tk->min_kern;
      }
    }

    return FT_Err_Ok;
  }

#else /* T1_CONFIG_OPTION_NO_AFM */

  /* ANSI C doesn't like empty source files */
  typedef int  _t1_afm_dummy;

#endif /* T1_CONFIG_OPTION_NO_AFM */

/* END */


text:
/****************************************************************************
 *
 * pfrgload.h
 *
 *   FreeType PFR glyph loader (specification).
 *
 * Copyright (C) 2002-2020 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.
 *
 */

#ifndef PFRGLOAD_H_
#define PFRGLOAD_H_

#include "pfrtypes.h"

FT_BEGIN_HEADER

  FT_LOCAL( void )
  pfr_glyph_init( PFR_Glyph       glyph,
                  FT_GlyphLoader  loader );

  FT_LOCAL( void )
  pfr_glyph_done( PFR_Glyph  glyph );

  FT_LOCAL( FT_Error )
  pfr_glyph_load( PFR_Glyph  glyph,
                  FT_Stream  stream,
                  FT_ULong   gps_offset,
                  FT_ULong   offset,
                  FT_ULong   size );

FT_END_HEADER

#endif /* PFRGLOAD_H_ */

/* END */


text:
/****************************************************************************
 *
 * pfrdrivr.c
 *
 *   FreeType PFR driver interface (body).
 *
 * Copyright (C) 2002-2020 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_STREAM_H
#include FT_SERVICE_PFR_H
#include FT_SERVICE_FONT_FORMAT_H
#include "pfrdrivr.h"
#include "pfrobjs.h"

#include "pfrerror.h"

  FT_CALLBACK_DEF( FT_Error )
  pfr_get_kerning( FT_Face     pfrface,     /* PFR_Face */
                   FT_UInt     left,
                   FT_UInt     right,
                   FT_Vector  *avector )
  {
    PFR_Face     face = (PFR_Face)pfrface;
    PFR_PhyFont  phys = &face->phy_font;

    (void)pfr_face_get_kerning( pfrface, left, right, avector );

    /* convert from metrics to outline units when necessary */
    if ( phys->outline_resolution != phys->metrics_resolution )
    {
      if ( avector->x != 0 )
        avector->x = FT_MulDiv( avector->x,
                                (FT_Long)phys->outline_resolution,
                                (FT_Long)phys->metrics_resolution );

      if ( avector->y != 0 )
        avector->y = FT_MulDiv( avector->y,
                                (FT_Long)phys->outline_resolution,
                                (FT_Long)phys->metrics_resolution );
    }

    return FT_Err_Ok;
  }

  /*
   * PFR METRICS SERVICE
   *
   */

  FT_CALLBACK_DEF( FT_Error )
  pfr_get_advance( FT_Face   pfrface,       /* PFR_Face */
                   FT_UInt   gindex,
                   FT_Pos   *anadvance )
  {
    PFR_Face  face  = (PFR_Face)pfrface;
    FT_Error  error = FT_ERR( Invalid_Argument );

    *anadvance = 0;

    if ( !gindex )
      goto Exit;

    gindex--;

    if ( face )
    {
      PFR_PhyFont  phys = &face->phy_font;

      if ( gindex < phys->num_chars )
      {
        *anadvance = phys->chars[gindex].advance;
        error      = FT_Err_Ok;
      }
    }

  Exit:
    return error;
  }

  FT_CALLBACK_DEF( FT_Error )
  pfr_get_metrics( FT_Face    pfrface,      /* PFR_Face */
                   FT_UInt   *anoutline_resolution,
                   FT_UInt   *ametrics_resolution,
                   FT_Fixed  *ametrics_x_scale,
                   FT_Fixed  *ametrics_y_scale )
  {
    PFR_Face     face = (PFR_Face)pfrface;
    PFR_PhyFont  phys = &face->phy_font;
    FT_Fixed     x_scale, y_scale;
    FT_Size      size = face->root.size;

    if ( anoutline_resolution )
      *anoutline_resolution = phys->outline_resolution;

    if ( ametrics_resolution )
      *ametrics_resolution = phys->metrics_resolution;

    x_scale = 0x10000L;
    y_scale = 0x10000L;

    if ( size )
    {
      x_scale = FT_DivFix( size->metrics.x_ppem << 6,
                           (FT_Long)phys->metrics_resolution );

      y_scale = FT_DivFix( size->metrics.y_ppem << 6,
                           (FT_Long)phys->metrics_resolution );
    }

    if ( ametrics_x_scale )
      *ametrics_x_scale = x_scale;

    if ( ametrics_y_scale )
      *ametrics_y_scale = y_scale;

    return FT_Err_Ok;
  }

  static
  const FT_Service_PfrMetricsRec  pfr_metrics_service_rec =
  {
    pfr_get_metrics,          /* get_metrics */
    pfr_face_get_kerning,     /* get_kerning */
    pfr_get_advance           /* get_advance */
  };

  /*
   * SERVICE LIST
   *
   */

  static const FT_ServiceDescRec  pfr_services[] =
  {
    { FT_SERVICE_ID_PFR_METRICS, &pfr_metrics_service_rec },
    { FT_SERVICE_ID_FONT_FORMAT, FT_FONT_FORMAT_PFR },
    { NULL, NULL }
  };

  FT_CALLBACK_DEF( FT_Module_Interface )
  pfr_get_service( FT_Module         module,
                   const FT_String*  service_id )
  {
    FT_UNUSED( module );

    return ft_service_list_lookup( pfr_services, service_id );
  }

  FT_CALLBACK_TABLE_DEF
  const FT_Driver_ClassRec  pfr_driver_class =
  {
    {
      FT_MODULE_FONT_DRIVER     |
      FT_MODULE_DRIVER_SCALABLE,

      sizeof ( FT_DriverRec ),

      "pfr",
      0x10000L,
      0x20000L,

      NULL,    /* module-specific interface */

      NULL,                     /* FT_Module_Constructor  module_init   */
      NULL,                     /* FT_Module_Destructor   module_done   */
      pfr_get_service           /* FT_Module_Requester    get_interface */
    },

    sizeof ( PFR_FaceRec ),
    sizeof ( PFR_SizeRec ),
    sizeof ( PFR_SlotRec ),

    pfr_face_init,              /* FT_Face_InitFunc  init_face */
    pfr_face_done,              /* FT_Face_DoneFunc  done_face */
    NULL,                       /* FT_Size_InitFunc  init_size */
    NULL,                       /* FT_Size_DoneFunc  done_size */
    pfr_slot_init,              /* FT_Slot_InitFunc  init_slot */
    pfr_slot_done,              /* FT_Slot_DoneFunc  done_slot */

    pfr_slot_load,              /* FT_Slot_LoadFunc  load_glyph */

    pfr_get_kerning,            /* FT_Face_GetKerningFunc   get_kerning  */
    NULL,                       /* FT_Face_AttachFunc       attach_file  */
    NULL,                       /* FT_Face_GetAdvancesFunc  get_advances */

    NULL,                       /* FT_Size_RequestFunc  request_size */
    NULL,                       /* FT_Size_SelectFunc   select_size  */
  };

/* END */