/****************************************************************************/
/*                                                                          */
/*  The FreeType project -- a free and portable quality TrueType renderer.  */
/*                                                                          */
/*  Copyright 1996-2000, 2003, 2004 by                                      */
/*  D. Turner, R.Wilhelm, and W. Lemberg                                    */
/*                                                                          */
/****************************************************************************/


#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H

  /* the following header shouldn't be used in normal programs */
#include FT_INTERNAL_DEBUG_H

  /* showing driver name */
#include FT_MODULE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DRIVER_H

#include "common.h"

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

#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H

  FT_Error  error;

  int  comma_flag  = 0;
  int  debug       = 0;
  int  trace_level = 0;
  int  name_tables = 0;
  FT_ULong hextag  = 0;


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


  void
  Print_Comma( const char*  message )
  {
    if ( comma_flag )
      printf( ", " );

    printf( "%s", message );
    comma_flag = 1;
  }


  static void
  usage( char*  execname )
  {
    fprintf( stderr, "\n" );
    fprintf( stderr, "ftdump: simple font dumper -- part of the FreeType project\n" );
    fprintf( stderr, "-----------------------------------------------------------\n" );
    fprintf( stderr, "\n" );
    fprintf( stderr, "Usage: %s [options] fontname\n", execname );
    fprintf( stderr, "\n" );
#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 0 && FREETYPE_PATCH <= 8
    fprintf( stderr, "  -d        enable debug information\n" );
#  ifdef FT_DEBUG_LEVEL_TRACE
    fprintf( stderr, "  -l level  trace level for debug information\n" );
#  endif
#endif
    fprintf( stderr, "  -n        print SFNT name tables\n" );
    fprintf( stderr, "  -x TABLE  hex dump of SFNT table `TABLE´\n" );
    fprintf( stderr, "\n" );

    exit( 1 );
  }

#ifndef min
#define min(a, b) (((a) < (b)) ? (a) : (b))
#endif
  
  void DumpMem(void* p, size_t size)
  {
#define MAXPRINT 16
    unsigned char *pHead = (unsigned char*)p;
    int i;                                  // loop counter
    unsigned char ch;                       // byte
    char printbuff[MAXPRINT+1];             // ASCII representation constructed here
    char valbuff[MAXPRINT*3+1];             // HEX values constructed here
    size_t off=0;

    printf("              ");
    for (i=0; i<MAXPRINT; i++)
        printf("%x", i % 16);

    putchar(' ');

    for (i=0; i<MAXPRINT; i++)
        printf("%02x ", i % 16);
    puts("");

    printf("              ");
    for (i=0; i<MAXPRINT; i++)
        putchar('-');

    putchar(' ');

    for (i=0; i<MAXPRINT; i++)
        printf("-- ");
    puts("");

    do
    {
        printf("    %8x ", off);

        valbuff[0]=0;
        for (i = 0; i < min((int)size, MAXPRINT); i++)
        {
            ch = *pHead++;
            printbuff[i] = (char)(isprint(ch) ? ch : ' ');
            sprintf(&valbuff[i*3], "%.2X ", ch);
        }
        while (i<MAXPRINT)
        {
            printbuff[i]='-';
            sprintf(&valbuff[i*3], "-- ");
            i++;
        }
        printbuff[i] = '\0';
        printf("%s %s\n", printbuff, valbuff);

        if (size>=MAXPRINT)
            size-=MAXPRINT;
        else
            size=0;
        off+=MAXPRINT;

    }
    while (size>0);
    puts("");
#undef MAXPRINT
  }
  

  void
  Print_Tables ( FT_Face face )
  {
    FT_UInt i;
    puts("tables present");
    for(i=0;;i++)
    {
        FT_ULong tag;
        FT_ULong length;
        error=FT_Sfnt_Table_Info(face, i, &tag, &length);
        if (error)
            break;
        printf(
            "   %c%c%c%c\n",
            (char)(tag>>24), (char)(tag>>16), (char)(tag>>8), (char)tag
            );
    }
  }
  
  void Print_OS2(FT_Face face)
  {
    void *p;
    TT_OS2* os2;
    
    puts("os/2 table entries");
    
    /* FUDGE:
       At the moment FT_Get_Sfnt_Table(face, ft_sfnt_os2) does NOT return NULL
       for a SING Glyphlet... so test for presence of a SING table, and
       gracefully exit if found
    */
    {
      FT_ULong length;
      
      length=0;
      error=FT_Load_Sfnt_Table( face, TTAG_SING, 0, NULL, &length );
      if(!error)
      {
        puts("   UNAVAILABLE [font contains SING Glyphlets]");
        return;
      }
    }

    p=FT_Get_Sfnt_Table(face, ft_sfnt_os2);
    if (p==NULL)
      PanicZ("Failed to get OS2 table");
      
    os2=(TT_OS2*)p;
    
    printf("  version:          %#x\n", os2->version);

  
    printf("  fsType:           %d (", os2->fsType);
    if ((os2->fsType & 0x0f)==0)
      printf("Installable Embedding");
    else
    {
      if (os2->fsType & 0x01)
        printf("Reserved bit set - but the spec says it must be clear!");
      if (os2->fsType & 0x02)
        printf("Restricted License embedding");
      if (os2->fsType & 0x4)
        printf("Preview & Print embedding");
      if (os2->fsType & 0x8)
        printf("Editable embedding");
    }
    printf(")\n");
  
    printf("  achVendID:        %c%c%c%c\n",
      os2->achVendID[0] ? os2->achVendID[0] : '~',
      os2->achVendID[1] ? os2->achVendID[1] : '~',
      os2->achVendID[2] ? os2->achVendID[2] : '~',
      os2->achVendID[3] ? os2->achVendID[3] : '~'
      );
    /* for seeing who is assigned to the vendor Ids, see */
    /* http://www.microsoft.com/typography/links/vendorlist.asp */
  }
  
  void
  Print_Name( FT_Face  face )
  {
    const char*  ps_name;
    

    printf( "font name entries\n" );

    /* XXX: Foundry?  Copyright?  Version? ... */

    printf( "   family:     %s\n", face->family_name );
    printf( "   style:      %s\n", face->style_name );
    
    ps_name = FT_Get_Postscript_Name( face );
    if ( ps_name == NULL )
      ps_name = "UNAVAILABLE";
    
    printf( "   postscript: %s\n", ps_name );
  }


  void
  Print_Type( FT_Face  face )
  {
    FT_ModuleRec*  module;


    printf( "font type entries\n" );

    module = &face->driver->root;
    printf( "   FreeType driver: %s\n", module->clazz->module_name );

    /* Is it better to dump all sfnt tag names? */
    printf( "   sfnt wrapped:    %s\n",
            FT_IS_SFNT( face ) ? (char *)"yes" : (char *)"no" );

    /* isScalable? */
    comma_flag = 0;
    printf( "   type:            " );
    if ( FT_IS_SCALABLE( face ) )
    {
      Print_Comma( "scalable" );
      if ( FT_HAS_MULTIPLE_MASTERS( face ) )
        Print_Comma( "multiple masters" );
    }
    if ( FT_HAS_FIXED_SIZES( face ) )
      Print_Comma( "fixed size" );
    printf( "\n" );
    
    /* quantity */
    printf("   how many glyphs: %ld\n", face->num_glyphs);

    /* Direction */
    comma_flag = 0;
    printf( "   direction:       " );
    if ( FT_HAS_HORIZONTAL( face ) )
      Print_Comma( "horizontal" );

    if ( FT_HAS_VERTICAL( face ) )
      Print_Comma( "vertical" );

    printf( "\n" );

    printf( "   fixed width:     %s\n",
            FT_IS_FIXED_WIDTH( face ) ? (char *)"yes" : (char *)"no" );

    printf( "   glyph names:     %s\n",
            FT_HAS_GLYPH_NAMES( face ) ? (char *)"yes" : (char *)"no" );
    
    if ( FT_IS_SCALABLE( face ) )
    {
      printf( "   EM size:         %d\n", face->units_per_EM );
      printf( "   global BBox:     (%ld,%ld):(%ld,%ld)\n",
              face->bbox.xMin, face->bbox.yMin,
              face->bbox.xMax, face->bbox.yMax );
      printf( "   ascent:          %d\n", face->ascender );
      printf( "   descent:         %d\n", face->descender );
      printf( "   text height:     %d\n", face->height );
    }
  }

  static const char*
  platform_id( int  id )
  {
    switch ( id )
    {
    case TT_PLATFORM_APPLE_UNICODE:
      return "Apple (Unicode)";
    case TT_PLATFORM_MACINTOSH:
      return "Macintosh";
    case TT_PLATFORM_ISO:
      return "ISO (deprecated)";
    case TT_PLATFORM_MICROSOFT:
      return "Microsoft";
    case TT_PLATFORM_CUSTOM:
      return "custom";
    case TT_PLATFORM_ADOBE:
      return "Adobe";

    default:
      return "UNKNOWN";
    }
  }


  static const char*
  name_id( int  id )
  {
    switch ( id )
    {
    case TT_NAME_ID_COPYRIGHT:
      return "copyright";
    case TT_NAME_ID_FONT_FAMILY:
      return "font family";
    case TT_NAME_ID_FONT_SUBFAMILY:
      return "font subfamily";
    case TT_NAME_ID_UNIQUE_ID:
      return "unique ID";
    case TT_NAME_ID_FULL_NAME:
      return "full name";
    case TT_NAME_ID_VERSION_STRING:
      return "version string";
    case TT_NAME_ID_PS_NAME:
      return "PostScript name";
    case TT_NAME_ID_TRADEMARK:
      return "trademark";

   /* the following values are from the OpenType spec */
    case TT_NAME_ID_MANUFACTURER:
      return "manufacturer";
    case TT_NAME_ID_DESIGNER:
      return "designer";
    case TT_NAME_ID_DESCRIPTION:
      return "description";
    case TT_NAME_ID_VENDOR_URL:
      return "vendor URL";
    case TT_NAME_ID_DESIGNER_URL:
      return "designer URL";
    case TT_NAME_ID_LICENSE:
      return "license";
    case TT_NAME_ID_LICENSE_URL:
      return "license URL";
    /* number 15 is reserved */
    case TT_NAME_ID_PREFERRED_FAMILY:
      return "preferred family";
    case TT_NAME_ID_PREFERRED_SUBFAMILY:
      return "preferred subfamily";
    case TT_NAME_ID_MAC_FULL_NAME:
      return "Mac full name";

   /* The following code is new as of 2000-01-21 */
    case TT_NAME_ID_SAMPLE_TEXT:
      return "sample text";

   /* This is new in OpenType 1.3 */
    case TT_NAME_ID_CID_FINDFONT_NAME:
      return "CID `findfont' name";

    default:
      return "UNKNOWN";
    }
  }


  static void
  put_ascii( FT_Byte*  string,
             FT_UInt   string_len,
             FT_UInt   indent )
  {
    FT_UInt  i, j;


    for ( j = 0; j < indent; j++ )
      putchar( ' ' );
    putchar( '"' );

    for ( i = 0; i < string_len; i++ )
    {
      switch ( string[i] )
      {
      case '\n':
        fputs( "\\n\"", stdout );
        if ( i + 1 < string_len )
        {
          putchar( '\n' );
          for ( j = 0; j < indent; j++ )
            putchar( ' ' );
          putchar( '"' );
        }
        break;
      case '\r':
        fputs( "\\r", stdout );
        break;
      case '\t':
        fputs( "\\t", stdout );
        break;
      case '\\':
        fputs( "\\\\", stdout );
        break;
      case '"':
        fputs( "\\\"", stdout );
        break;

      default:
        putchar( string[i] );
        break;
      }
    }
    if ( string[i - 1] != '\n' )
      putchar( '"' );
  }


  static void
  put_unicode_be16( FT_Byte*  string,
                    FT_UInt   string_len,
                    FT_UInt   indent )
  {
    FT_Int   ch = 0;
    FT_UInt  i, j;


    for ( j = 0; j < indent; j++ )
      putchar( ' ' );
    putchar( '"' );

    for ( i = 0; i < string_len; i += 2 )
    {
      ch = ( string[i] << 8 ) | string[i + 1];

      switch ( ch )
      {
      case '\n':
        fputs( "\\n\"", stdout );
        if ( i + 2 < string_len )
        {
          putchar( '\n' );
          for ( j = 0; j < indent; j++ )
            putchar( ' ' );
          putchar( '"' );
        }
        break;
      case '\r':
        fputs( "\\r", stdout );
        break;
      case '\t':
        fputs( "\\t", stdout );
        break;
      case '\\':
        fputs( "\\\\", stdout );
        break;
      case '"':
        fputs( "\\\"", stdout );
        break;

      case 0x00A9:
        fputs( "(c)", stdout );
        break;
      case 0x00AE:
        fputs( "(r)", stdout );
        break;

      case 0x2013:
        fputs( "--", stdout );
        break;
      case 0x2019:
        fputs( "\'", stdout );
        break;

      case 0x2122:
        fputs( "(tm)", stdout );
        break;

      default:
        if ( ch < 256 )
          putchar( ch );
        else
          printf( "\\U+%04X", ch );
        break;
      }
    }
    if ( ch != '\n' )
      putchar( '"' );
  }


  void
  Print_Sfnt_Names( FT_Face  face )
  {
    FT_SfntName  name;
    FT_UInt      num_names, i;


    printf( "font string entries\n" );

    num_names = FT_Get_Sfnt_Name_Count( face );
    for ( i = 0; i < num_names; i++ )
    {
      error = FT_Get_Sfnt_Name( face, i, &name );
      if ( error == FT_Err_Ok )
      {
        printf( "   %-15s [%s]", name_id( name.name_id ),
                                 platform_id( name.platform_id ) );

        switch ( name.platform_id )
        {
        case TT_PLATFORM_APPLE_UNICODE:
          switch ( name.encoding_id )
          {
          case TT_APPLE_ID_DEFAULT:
          case TT_APPLE_ID_UNICODE_1_1:
          case TT_APPLE_ID_ISO_10646:
          case TT_APPLE_ID_UNICODE_2_0:
            put_unicode_be16( name.string, name.string_len, 6 );
            break;

          default:
            printf( "{unsupported encoding %d}", name.encoding_id );
            break;
          }
          break;

        case TT_PLATFORM_MACINTOSH:
          if ( name.language_id != TT_MAC_LANGID_ENGLISH )
            printf( " (language=%d)", name.language_id );
          fputs( ":\n", stdout );

          switch ( name.encoding_id )
          {
          case TT_MAC_ID_ROMAN:
            /* FIXME: convert from MacRoman to ASCII/ISO8895-1/whatever */
            /* (MacRoman is mostly like ISO8895-1 but there are         */
            /* differences)                                             */
            put_ascii( name.string, name.string_len, 6 );
            break;

          default:
            printf( "{unsupported encoding %d}", name.encoding_id );
            break;
          }

          break;

        case TT_PLATFORM_ISO:
          switch ( name.encoding_id )
          {
          case TT_ISO_ID_7BIT_ASCII:
          case TT_ISO_ID_8859_1:
            put_ascii( name.string, name.string_len, 6 );
            break;

          case TT_ISO_ID_10646:
            put_unicode_be16( name.string, name.string_len, 6 );
            break;

          default:
            printf( "{unsupported encoding %d}", name.encoding_id );
            break;
          }
          break;

        case TT_PLATFORM_MICROSOFT:
          if ( name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES )
            printf( " (language=0x%04x)", name.language_id );
          fputs( ":\n", stdout );

          switch ( name.encoding_id )
          {
          /* TT_MS_ID_SYMBOL_CS is supposed to be Unicode, according to */
          /* information from the MS font development team              */
          case TT_MS_ID_SYMBOL_CS:
          case TT_MS_ID_UNICODE_CS:
            put_unicode_be16( name.string, name.string_len, 6 );
            break;

          default:
            printf( "{unsupported encoding %d}", name.encoding_id );
            break;
          }

          break;

        default:
          printf( "{unsupported platform}" );
          break;
        }

        printf( "\n" );
      }
    }
  }


  void
  Print_Fixed( FT_Face  face )
  {
    int  i;


    /* num_fixed_size */
    printf( "fixed size\n" );

    /* available size */
    for ( i = 0; i < face->num_fixed_sizes; i++ )
    {
      FT_Bitmap_Size*  bsize = face->available_sizes + i;


      printf( "   %3d: height %d, width %d\n",
              i, bsize->height, bsize->width );
      printf( "        size %.3f, x_ppem %.3f, y_ppem %.3f\n",
              bsize->size / 64.0,
              bsize->x_ppem / 64.0, bsize->y_ppem / 64.0 );
    }
  }


  void
  Print_Charmaps( FT_Face  face )
  {
    int  i;


    /* CharMaps */
    printf( "charmaps\n" );

    for( i = 0; i < face->num_charmaps; i++ )
    {
      printf( "   %d: platform: %d, encoding: %d\n",
              i,
              face->charmaps[i]->platform_id,
              face->charmaps[i]->encoding_id );
    }
  }


  int
  main( int    argc,
        char*  argv[] )
  {
    int    i, file;
    char   filename[128 + 4];
    char   alt_filename[128 + 4];
    char*  execname;
    int    num_faces;
    int    option;

    FT_Library  library;      /* the FreeType library */
    FT_Face     face;         /* the font face        */


    execname = ft_basename( argv[0] );

    while ( 1 )
    {
      option = getopt( argc, argv, "dl:x:n" );

      if ( option == -1 )
        break;

      switch ( option )
      {
      case 'd':
        debug = 1;
        break;

      case 'l':
        trace_level = atoi( optarg );
        if ( trace_level < 1 || trace_level > 7 )
          usage( execname );
        break;

      case 'n':
        name_tables = 1;
        break;
        
      case 'x':
        if (hextag) /* if we've already been given a x option */
          usage( execname ); /* error */

        if (strlen(optarg)==4)
          hextag=FT_MAKE_TAG( optarg[0], optarg[1], optarg[2], optarg[3] );
        else if (strlen(optarg)==3)
          hextag=FT_MAKE_TAG( optarg[0], optarg[1], optarg[2], ' ');
        else
          usage(execname);
        break;

      default:
        usage( execname );
        break;
      }
    }

    argc -= optind;
    argv += optind;

    if ( argc != 1 )
      usage( execname );

#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 0 && FREETYPE_PATCH <= 8
    if ( debug )
    {
#  ifdef FT_DEBUG_LEVEL_TRACE
      FT_SetTraceLevel( trace_any, (FT_Byte)trace_level );
#  else
      trace_level = 0;
#  endif
    }
#elif 0
       /* "setenv/putenv" is not ANSI and I don't want to mess */
       /* with this portability issue right now                */
    if ( debug )
    {
      char  temp[32];
      

      sprintf( temp, "any:%d", trace_level );
      setenv( "FT2_DEBUG", temp );
    }
#endif

    file = 0;

    /* Initialize engine */
    error = FT_Init_FreeType( &library );
    if ( error )
      PanicZ( "Could not initialize FreeType library" );

    filename[128]     = '\0';
    alt_filename[128] = '\0';

    strncpy( filename, argv[file], 128 );
    strncpy( alt_filename, argv[file], 128 );

    /* try to load the file name as is, first */
    error = FT_New_Face( library, argv[file], 0, &face );
    if ( !error )
      goto Success;

#ifndef macintosh
    i = strlen( argv[file] );
    while ( i > 0 && argv[file][i] != '\\' && argv[file][i] != '/' )
    {
      if ( argv[file][i] == '.' )
        i = 0;
      i--;
    }

    if ( i >= 0 )
    {
      strncpy( filename + strlen( filename ), ".ttf", 4 );
      strncpy( alt_filename + strlen( alt_filename ), ".ttc", 4 );
    }
#endif

    /* Load face */
    error = FT_New_Face( library, filename, 0, &face );
    if ( error )
      PanicZ( "Could not open face." );

  Success:
    num_faces = face->num_faces;
    FT_Done_Face( face );

    printf( "There %s %d %s in this file.\n",
            num_faces == 1 ? (char *)"is" : (char *)"are",
            num_faces,
            num_faces == 1 ? (char *)"face" : (char *)"faces" );

    for ( i = 0; i < num_faces; i++ )
    {
      error = FT_New_Face( library, filename, i, &face );
      if ( error )
        PanicZ( "Could not open face." );

      printf( "\n----- Face number: %d -----\n\n", i );
      
      if ( hextag && FT_IS_SFNT( face ))
      {
        FT_ULong length;
        FT_Byte *buffer;
        
        length=0;
        error=FT_Load_Sfnt_Table( face, hextag, 0, NULL, &length );
        if(error)
          PanicZ("Failed to load table");
        
        buffer=malloc(length);
        error=FT_Load_Sfnt_Table( face, hextag, 0, buffer, &length );
        if(error)
          PanicZ("Failed to load table");
        DumpMem(buffer, length);
        free(buffer);
        
        continue; /* don't dump anything else when doing a hex dump */
      }
      
      if ( FT_IS_SFNT( face ) )
        Print_Tables( face );
    
      printf( "\n" );
      Print_Name( face );
      printf( "\n" );
      Print_Type( face );
      
      if ( FT_IS_SFNT( face ) )
      {
        printf( "\n" );
        Print_OS2( face );
      }
      
      if ( name_tables && FT_IS_SFNT( face ) )
      {
        printf( "\n" );
        Print_Sfnt_Names( face );
      }

      if ( face->num_fixed_sizes )
      {
        printf( "\n" );
        Print_Fixed( face );
      }

      if ( face->num_charmaps )
      {
        printf( "\n" );
        Print_Charmaps( face );
      }

      FT_Done_Face( face );
    }

    FT_Done_FreeType( library );

    exit( 0 );      /* for safety reasons */
    return 0;       /* never reached */
  }


/* End */
