shithub: freetype+ttf2subf

ref: c06eb3b73a07f18e24634bd96d3ef791bc43a3dc
dir: /demos/src/t1dump.c/

View raw version
/****************************************************************************/
/*                                                                          */
/* t1dump.c                                                            1.0  */
/*                                                                          */
/* Copyright 1999 - The FreeType Project http://www.freetype.org            */
/*                                                                          */
/* T1Dump is a very simply Type 1 font dumper. It can be used to            */
/* write the following information to the standard ouput, or any            */
/* given file:                                                              */
/*                                                                          */
/*  - a description of the font file (including name, properties, etc..)    */
/*  - the decrypted private dictionary, 'as is', i.e. in binary form        */
/*  - the stream of tokens from the font file (useful to debug the          */
/*    Type1 driver or to look at the font's internal structure..)           */
/*  - the charstring commands of a given subroutine                         */
/*  - the charstring commands of a given glyph                              */
/*  - the encoding                                                          */
/*  - the glyph names                                                       */
/*                                                                          */

#include <stdio.h>
#include <stdlib.h>

#include "freetype.h"
#include <t1tokens.h>
#include <t1gload.h>
#include <t1load.h>
#include <t1parse.h>

FT_Library      library;    /* root library object */
FT_Face         face;       /* truetype face */
T1_Face         t1_face;
FT_Error        error;
FILE*           target;

  void Panic( const char* message )
  {
    fprintf( stderr, "%s\n  error code = 0x%04x\n", message, error );
    exit(1);
  }


/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********              DUMP FONT INFORMATION                    *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


  static
  T1_Error  Dump_Font_Info( void )
  {
    T1_FontInfo*  info = &t1_face->font_info;
    T1_Private*   priv = &t1_face->private_dict;
    T1_Int        n;

    fprintf( target, "Font Name    : %s\n", t1_face->font_name );

    fprintf( target, "Version      : %s\n", info->version );
    fprintf( target, "Full Name    : %s\n", info->full_name );
    fprintf( target, "Family       : %s\n", info->family_name );
    fprintf( target, "Weight       : %s\n", info->weight );
    fprintf( target, "Italic angle : %ld\n", info->italic_angle );

    fprintf( target, "Fixed pitch  : %s\n",
                     info->is_fixed_pitch ? "yes" : "no" );

    fprintf( target, "Underline    : pos %d, thickness %d\n",
                     info->underline_position,
                     info->underline_thickness );

    fprintf( target, "Unique ID    : %d\n", priv->unique_id );
    fprintf( target, "lenIV        : %d\n", priv->lenIV );

    fprintf( target, "blues        : [" );
    for ( n = 0; n < priv->num_blues; n++ )
    fprintf( target, " %d", priv->blue_values[n] );
    fprintf( target, " ]\n" );

    fprintf( target, "other blues  : [" );
    for ( n = 0; n < priv->num_other_blues; n++ )
    fprintf( target, " %d", priv->other_blues[n] );
    fprintf( target, " ]\n" );

    fprintf( target, "family blues : [" );
    for ( n = 0; n < priv->num_family_blues; n++ )
    fprintf( target, " %d", priv->family_blues[n] );
    fprintf( target, " ]\n" );

    fprintf( target, "family other : [" );
    for ( n = 0; n < priv->num_family_other_blues; n++ )
    fprintf( target, " %d", priv->family_other_blues[n] );
    fprintf( target, " ]\n" );

    fprintf( target, "Blue scale   : %f\n", priv->blue_scale*1.0/65536.0 );
    fprintf( target, "Blue shift   : %d\n", priv->blue_shift );
    fprintf( target, "Blue fuzz    : %d\n", priv->blue_fuzz );

    fprintf( target, "Std width    : %d\n", priv->standard_width );
    fprintf( target, "Std height   : %d\n", priv->standard_height );
    fprintf( target, "Force bold   : %s\n", priv->force_bold ? "yes" : "no" );
    fprintf( target, "Round stem   : %s\n", priv->round_stem_up ? "yes" : "no" );

    fprintf( target, "Stem snap W  : [" );
    for ( n = 0; n < priv->num_snap_widths; n++ )
    fprintf( target, " %d", priv->stem_snap_widths[n] );
    fprintf( target, " ]\n" );

    fprintf( target, "Stem snap H  : [" );
    for ( n = 0; n < priv->num_snap_heights; n++ )
    fprintf( target, " %d", priv->stem_snap_heights[n] );
    fprintf( target, " ]\n" );

    fprintf( target, "Language     : %ld\n", priv->language_group );
    fprintf( target, "Password     : %ld\n", priv->password );
    fprintf( target, "Min feature  : [ %d %d ]\n",
             priv->min_feature[0],
             priv->min_feature[1] );

    fprintf( target, "Font BBOX    : [ %ld %ld %ld %ld ]\n",
                     t1_face->font_bbox.xMin,
                     t1_face->font_bbox.yMin,
                     t1_face->font_bbox.xMax,
                     t1_face->font_bbox.yMax );

    fprintf( target, "Font matrix  : [ %f %f %f %f ]\n",
                     1.0*t1_face->font_matrix.xx/65536000.0,
                     1.0*t1_face->font_matrix.xy/65536000.0,
                     1.0*t1_face->font_matrix.yx/65536000.0,
                     1.0*t1_face->font_matrix.yy/65536000.0 );
#if 0
    fprintf( target,
    fprintf( target,
    fprintf( target,
    fprintf( target,
    fprintf( target,
    fprintf( target,
#endif
    fprintf( target, "Num glyphs   : %d\n", t1_face->num_glyphs );
    fprintf( target, "Num subrs    : %d\n", t1_face->num_subrs );
    
    return 0;
  }



/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********              DUMP PRIVATE DICT IN RAW FORM            *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


  static
  T1_Error  parse_int( T1_Tokenizer  tokzer,
                       T1_Long*      result )
  {
    T1_Bool    sign = 0;
    T1_Long    sum  = 0;
    T1_Token*  token = &tokzer->token;
    T1_Byte*   base  = tokzer->base + token->start;
    T1_Byte*   limit = base + token->len;

    if (base >= limit)
      goto Fail;

    /* check sign */
    if ( *base == '+' )
      base++;

    else if ( *base == '-' )
    {
      sign++;
      base++;
    }

    /* parse digits */
    if ( base >= limit )
      goto Fail;

    do
    {
      sum = ( 10*sum + (*base++ - '0') );

    } while (base < limit);

    if (sign)
      sum = -sum;

    *result = sum;
    return T1_Err_Ok;

  Fail:
    *result = 0;
    return T1_Err_Syntax_Error;
  }





  static
  T1_Error  Dump_Private_Dict( const char*  filename )
  {
    struct FT_StreamRec_  stream_rec;
    FT_Stream             stream = &stream_rec;
    T1_Error              error;
    T1_Tokenizer          tokenizer;

    error = FT_New_Stream( filename, stream );
    if (error) return error;

    stream->memory = library->memory;
    
    error = New_Tokenizer( stream, &tokenizer );
    if (error) goto Exit;

    /* go directly to the Private dictionary */
    error = Open_PrivateDict( tokenizer );
    if (error)
      Panic( "Could not open private dictionary !!" );

    /* Write it to the target file */
    fwrite( tokenizer->base, tokenizer->limit, 1, target );

  Exit:
    if (stream->close)
      stream->close(stream);

    return error;
  }



/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********              DUMP TYPE 1 TOKEN STREAM                 *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


  static
  T1_Error  Dump_Type1_Tokens( const char* filename )
  {
    struct FT_StreamRec_  stream_rec;
    FT_Stream             stream = &stream_rec;

    T1_Error      error;
    T1_Tokenizer  tokenizer;

    error = FT_New_Stream( filename, stream );
    if (error) return error;

    stream->memory = library->memory;
    
    error = New_Tokenizer( stream, &tokenizer );
    if (error) goto Exit;

    /* Dump the first segment of the Type1 font */
    do
    {
      T1_Token*  token;
      T1_String  temp_string[128];
      T1_Int     len;

      error = Read_Token( tokenizer );
      if (error) { error = 0; break; }

      /* dump the token */
      token = &tokenizer->token;
      len   = token->len;
      if (len > 127) len = 127;

      strncpy( temp_string,
               (T1_String*)(tokenizer->base + token->start),
               len );
      temp_string[len] = '\0';

      fprintf( target, "%s\n", temp_string );

	  /* Exit the loop when we encounter a "currentfile" token */
	  if ( token->kind  == tok_keyword &&
	       token->kind2 == key_currentfile )
		break;

    } while (1);

    error = Open_PrivateDict( tokenizer );
    if (error)
      Panic( "** could not open private dictionary **\n" );
    else
    {
      T1_Int   num = 0;
      T1_Bool  last_num = 0;

      do
      {
        T1_Token*  token;
        T1_String  temp_string[128];
        T1_Int     len;

        error = Read_Token( tokenizer );
        if (error) { error = 0; break; }

        /* dump the token */
        token = &tokenizer->token;
        len   = token->len;
        if (len > 127) len = 127;

        strncpy( temp_string,
                 (T1_String*)(tokenizer->base + token->start),
                 len );
        temp_string[len] = '\0';

        /* detect "RD" uses */
        if ( token->kind  == tok_keyword          &&
             ( token->kind2 == key_RD ||
               token->kind2 == key_RD_alternate ) &&
             last_num                             )
        {
          fprintf( target, "%s [%d binary bytes] ", temp_string, num );
          tokenizer->cursor += num;
        }
        else
        {
          fprintf( target, "%s\n", temp_string );

          /* exit dump when we encounter a 'closefile' */
          if ( token->kind  == tok_keyword   &&
               token->kind2 == key_closefile )
            break;

          /* record numerical value if any */
          if ( token->kind == tok_number )
          {
            T1_Long  sum;

            if ( !parse_int( tokenizer, &sum ) )
            {
              num      = sum;
              last_num = 1;
            }
            else
              last_num = 0;
          }
          else
            last_num = 0;
        }

      } while (1);
    }

  Exit:
    if (stream->close)
      stream->close(stream);
    return error;
  }


/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********                DUMP CHARACTER ENCODING                *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


  static
  void  Dump_Encoding( void )
  {
	T1_Encoding*  encode = &t1_face->encoding;
	int           n;

	fprintf( target, "characters count = %d\n", encode->num_chars );

	fprintf( target, "first code = %d, last code = %d\n",
			 encode->code_first, encode->code_last );

    for ( n = 0; n < encode->num_chars; n++ )
    {
	  int  code = (int)encode->char_index[n];

	  if (code || n == 0)
	    fprintf( target, "%3d %s\n", n, t1_face->glyph_names[code] );
    }
  }


/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********        DUMP SUBROUTINES AND GLYPH CHARSTRINGS         *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


  static
  void  Dump_CharStrings( T1_Byte*  base,
                          T1_Int    len )
  {
    T1_Byte*   cur   = base;
    T1_Byte*   limit = base + len;
    T1_String  temp_name[128];
    T1_String* string;

    T1_Int     x = 0;

    while ( cur < limit )
    {
      switch (*cur++)
      {
        case 1:  string = "hstem";     break;

        case 3:  string = "vstem";     break;
        case 4:  string = "vmoveto";   break;
        case 5:  string = "rlineto";   break;
        case 6:  string = "hlineto";   break;
        case 7:  string = "vlineto";   break;
        case 8:  string = "rrcurveto"; break;
        case 9:  string = "closepath"; break;
        case 10: string = "callsubr";  break;
        case 11: string = "return";    break;

        case 13: string = "hsbw";      break;
        case 14: string = "endchar";   break;

        case 21: string = "rmoveto";   break;
        case 22: string = "hmoveto";   break;

        case 30: string = "vhcurveto"; break;
        case 31: string = "hvcurveto";  break;

        case 12:
          {
            if (cur > limit)
              Panic( "invalid charstrings stream\n" );

            switch (*cur++)
            {
              case 0:  string = "dotsection";      break;
              case 1:  string = "vstem3";          break;
              case 2:  string = "hstem3";          break;
              case 6:  string = "seac";            break;
              case 7:  string = "sbw";             break;
              case 12: string = "div";             break;
              case 16: string = "callothersubr";   break;
              case 17: string = "pop";             break;
              case 33: string = "setcurrentpoint"; break;

              default:
                sprintf( temp_name, "escape(12)+unknown(%d)", cur[1] );
                string = temp_name;
            }
          }
          break;

        case 255:    /* four bytes integer */
          {
            T1_Long  sum;

            if (cur+4 > limit)
              Panic( "invalid charstrings stream\n" );

            sum = ((long)cur[0] << 24) |
                  ((long)cur[1] << 16) |
                  ((long)cur[2] << 8)  |
                         cur[3];
            sprintf( temp_name, "%ld ", sum );
            string = temp_name;
            cur += 4;
          }
          break;

        default:
          if (cur[-1] >= 32)
          {
            if (cur[-1] < 247)
            {
              sprintf( temp_name, "%ld", (long)cur[-1] - 139 );
            }
            else if (cur[-1] < 251)
            {
              cur++;
              sprintf( temp_name, "%ld",
                       ((long)(cur[-2]-247) << 8) + cur[-1] + 108 );
            }
            else
            {
              cur++;
              sprintf( temp_name, "%ld",
                       -((long)(cur[-2]-251) << 8) - cur[-1] - 108 );
            }
            string = temp_name;
          }
          else
          {
            sprintf( temp_name, "unknown(%d)", cur[-1] );
            string = temp_name;
          }
      }

      /* now print the charstring command */
      {
        int  len = strlen(string)+1;

        if ( x+len > 60 )
        {
          x = 0;
          fprintf( target, "\n" );
        }
        else
          fprintf( target, " " );

        fprintf( target, "%s", string );
        x += len;
      }
    }
  }



  static
  void  Dump_Glyph( int  glyph_index )
  {
    fprintf( target, "glyph name: %s\n", t1_face->glyph_names[glyph_index] );
    Dump_CharStrings( t1_face->charstrings     [glyph_index],
                      t1_face->charstrings_len [glyph_index] );
  }

  static
  void  Dump_Subrs( int  subrs_index )
  {
    Dump_CharStrings( t1_face->subrs    [ subrs_index ],
                      t1_face->subrs_len[ subrs_index ] );
  }



/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********                EXECUTE GLYPH CHARSTRINGS              *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


         static
         T1_Error  operator_endchar( T1_Builder*  builder )
         {
           (void)builder;
           fprintf( target, "endchar\n" );
           return 0;
         }
       
       
         static
         T1_Error  operator_sbw( T1_Builder*  builder,
                                 T1_Pos       sbx,
                                 T1_Pos       sby,
                                 T1_Pos       wx,
                                 T1_Pos       wy )
         {
           (void)builder;
           fprintf( target, "set bearing [%ld,%ld] width [%ld,%ld]\n",
                            sbx, sby, wx, wy );
           return 0;
         }
       
#if 0
         static
         T1_Error  operator_seac( T1_Builder*  builder,
                                  T1_Pos       asb,
                                  T1_Pos       adx,
                                  T1_Pos       ady,
                                  T1_Int       bchar,
                                  T1_Int       achar )
         {
           (void)builder;
           fprintf( target, "accented char: %ld [%ld,%ld] b=%d, a=%d\n",
                            asb, adx, ady, bchar, achar );
           return 0;
         }
#endif
       
         static
         T1_Error  operator_closepath( T1_Builder*  builder )
         {
           (void)builder;
           fprintf( target, "closepath\n" );
           return 0;
         }
       
       
         static
         T1_Error  operator_rlineto( T1_Builder*  builder,
                                     T1_Pos       dx,
                                     T1_Pos       dy )
         {
           (void)builder;
           fprintf( target, "%ld %ld rlineto\n", dx, dy );
           return 0;
         }
       
       
         static
         T1_Error  operator_rmoveto( T1_Builder*  builder,
                                     T1_Pos       dx,
                                     T1_Pos       dy )
         {
           (void)builder;
           fprintf( target, "%ld %ld rmoveto\n", dx, dy );
           return 0;
         }
       
       
         static
         T1_Error  operator_rrcurveto( T1_Builder*  builder,
                                       T1_Pos       dx1,
                                       T1_Pos       dy1,
                                       T1_Pos       dx2,
                                       T1_Pos       dy2,
                                       T1_Pos       dx3,
                                       T1_Pos       dy3 )
         {
           (void)builder;
           fprintf( target, "%ld %ld %ld %ld %ld %ld rrcurveto\n",
                    dx1, dy1, dx2, dy2, dx3, dy3 );
           return 0;
         }
       
       
         static
         T1_Error  operator_dotsection( T1_Builder*  builder )
         {
           (void)builder;
           fprintf( target, "dotsection\n" );
           return 0;
         }
       
       
         static
         T1_Error  operator_stem( T1_Builder*  builder,
                                  T1_Pos       pos,
                                  T1_Pos       width,
                                  T1_Bool      vertical )
         {
           (void)builder;
           fprintf( target, "%ld %ld %s\n", pos, width,
                    vertical ? "vstem" : "hstem" );
           return 0;
         }
       
       
         static
         T1_Error  operator_stem3( T1_Builder*  builder,
                                   T1_Pos       pos0,
                                   T1_Pos       width0,
                                   T1_Pos       pos1,
                                   T1_Pos       width1,
                                   T1_Pos       pos2,
                                   T1_Pos       width2,
                                   T1_Bool      vertical )
         {
           (void)builder;
           fprintf( target, "%ld %ld %ld %ld %ld %ld %s\n",
                    pos0, width0, pos1, width1, pos2, width2,
                    vertical ? "vstem3" : "hstem3" );
           return 0;
         }
       
       
#if 0
         static
         T1_Error  operator_flex( T1_Builder*  builder,
                                  T1_Pos       threshold,
                                  T1_Pos       end_x,
                                  T1_Pos       end_y )
         {
           (void)builder;
           fprintf( target, "%ld %ld %ld flex\n", threshold, end_x, end_y );
           return 0;
         }
#endif  
       
         static
         T1_Error  operator_changehints( T1_Builder*  builder )
         {
           (void)builder;
           fprintf( target, "-- change hints --\n" );
           return 0;
         }
       
       
  static
  T1_Error  Execute_CharString( int  glyph_index )
  {
    static const T1_Builder_Funcs   builds =
    {
      operator_endchar,
      operator_sbw,
      operator_closepath,
      operator_rlineto,
      operator_rmoveto,
      operator_rrcurveto,
    };

    static const T1_Hinter_Funcs  hints =
    {
      operator_dotsection,
      operator_changehints,
      operator_stem,
      operator_stem3,
    };

    T1_Decoder  decoder;
    T1_Error    error;

    T1_Init_Decoder( &decoder, &hints );
    T1_Init_Builder( &decoder.builder, t1_face, 0, 0, &builds );

    error = T1_Parse_CharStrings( &decoder,
                                  t1_face->charstrings    [glyph_index],
                                  t1_face->charstrings_len[glyph_index],
                                  t1_face->num_subrs,
                                  t1_face->subrs,
                                  t1_face->subrs_len );
   return error;                               
  }








/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********                  DEBUG FONT LOADING                   *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/

  static
  T1_Error  Debug_Type1_Font( const char*  filename )
  {
    struct FT_StreamRec_  stream_rec;
    T1_FaceRec    t1facerec;
    T1_Tokenizer  tokenizer;
    T1_Parser     parser;
    T1_Error      error;
    FT_Stream     stream = &stream_rec;

    error = FT_New_Stream( filename, stream );
    if (error) goto Exit;

    stream->memory = library->memory;
    
    /* create an empty face record */
    memset( &t1facerec, 0, sizeof(t1facerec) );
    t1facerec.root.memory = library->memory;
    t1facerec.root.stream = stream;

    t1_face = &t1facerec;

    /* open the tokenizer, this will also check the font format */
	error = New_Tokenizer( stream, &tokenizer );
    if (error) goto Fail;

    /* Now, load the font program into the face object */
    Init_T1_Parser( &parser, t1_face, tokenizer );

    /* force token dump */
    parser.dump_tokens = 1;

    error = Parse_T1_FontProgram( &parser );

    Done_Tokenizer( tokenizer );

  Fail:
    if (stream->close)
      stream->close( stream );
  Exit:
    return error;
  }

/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/**********                                                       *********/
/**********                                                       *********/
/**********                     MAIN PROGRAM                      *********/
/**********                                                       *********/
/**********                                                       *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


  static
  void Usage()
  {
    fprintf( stderr, "t1dump       -  a simple Type 1 font dumper\n" );
    fprintf( stderr, "(c) The FreeType project - www.freetype.org\n" );
    fprintf( stderr, "-------------------------------------------\n\n" );

    fprintf( stderr, "usage :   t1dump  [options] fontfile(.pfb|.pfa)\n\n" );

    fprintf( stderr, "    options\n" );
    fprintf( stderr, "          -o filename : dumps to a specific file\n" );
    fprintf( stderr, "          -g index    : dump glyph charstring\n" );
    fprintf( stderr, "          -s index    : dump subrs charstring\n" );
    fprintf( stderr, "          -x index    : execute glyph charstring\n" );
	fprintf( stderr, "          -e          : dump encoding\n" );
    fprintf( stderr, "          -t          : dumps the Type 1 token stream\n" );
    fprintf( stderr, "          -d          : debug font loading\n" );
    fprintf( stderr, "          -p          : dumps private dictionary 'as is'\n\n" );

    exit(1);
  }


typedef enum Request_
{
  req_dump_info,
  req_dump_private,
  req_dump_tokens,
  req_dump_encoding,
  req_dump_glyph,
  req_dump_subr,
  req_load_font,
  req_execute_glyph,
  req_debug_font

} Request;


static char*    file_name;
static int      glyph_index;
static Request  request = req_dump_info;

static FT_Driver t1_driver;

  int  main( int argc, char**  argv )
  {
    char  valid;


    /* Check number of arguments */
    if ( argc < 2 ) Usage();

    /* Check options */
    target = stdout;

    argv++;
    while (argv[0][0] == '-')
    {
      valid = 0;
      switch (argv[0][1])
      {
        case 'p':
          request = req_dump_private;
          valid   = 1;
          break;

        case 't':
          request = req_dump_tokens;
          valid   = 1;
          break;

        case 'e':
          request = req_dump_encoding;
          valid   = 1;
          break;

        case 'd':
          request = req_debug_font;
          valid   = 1;
          break;

        case 'o':
          if (argc < 2) Usage();
          target = fopen( argv[1], "w" );
          if (!target)
            Panic( "Could not open/create destination file" );
          argv++;
          argc--;
          valid = 1;
          break;

        case 'g':
        case 's':
        case 'x':
          if (argc < 2) Usage();
          if ( sscanf( argv[1], "%d", &glyph_index ) != 1 )
            Usage();

          switch (argv[0][1])
          {
            case 'g': request = req_dump_glyph;    break;
            case 's': request = req_dump_subr;     break;
            case 'x': request = req_execute_glyph; break;
          }
          argv++;
          argc--;
          valid = 1;
          break;

        default:
          ;
      }

      if (valid)
      {
        argv++;
        argc--;
        if (argc < 2) Usage();
      }
      else
        break;
    }

    /* Get file name */
    file_name = argv[0];

    /* Instead of calling FT_Init_FreeType, we set up our own system  */
    /* object and library. This is reserved for FreeType 2 wizards !! */

    /* Init library, read face object, get driver, create size */
    error = FT_Init_FreeType( &library );
    if (error) Panic( "could not initialise FreeType library" );

    t1_driver = FT_Get_Driver( library, "type1" );
    if (!t1_driver) Panic( "no Type1 driver in current FreeType lib" );

    error = FT_New_Face( library, file_name, 0, &face );
    if (error) Panic( "could not find/open/create font file" );
    
    if (face->driver != t1_driver)
      Panic( "font format is not Type 1 !" );

    switch (request)
    {
      case req_dump_private:
        error = Dump_Private_Dict(file_name);
        break;

      case req_dump_tokens:
        error = Dump_Type1_Tokens(file_name);
        break;

      case req_debug_font:
        error = Debug_Type1_Font(file_name);
        break;

      default:
        error = FT_New_Face( library, file_name, 0, &face );
        if (error) Panic( "could not load Type 1 font" );

        t1_face = (T1_Face)face;

        /* check glyph index, it is 0 by default */
        if ( glyph_index < 0 || glyph_index >= t1_face->num_glyphs )
          Panic( "invalid glyph index\n" );

        switch (request)
        {
          case req_dump_glyph:
            Dump_Glyph( glyph_index );
            break;

          case req_dump_subr:
            Dump_Subrs( glyph_index );
            break;

          case req_execute_glyph:
            Execute_CharString( glyph_index );
            break;

          case req_dump_encoding:
            Dump_Encoding();
            break;

          default:
            Dump_Font_Info();
        }
    }

    if (error) Panic( "could not dump Type 1 font" );

    FT_Done_FreeType( library );
    return 0;
  }