/****************************************************************************/
/*                                                                          */
/*  The FreeType project -- a free and portable quality TrueType renderer.  */
/*                                                                          */
/*  Copyright 1996, 1997 by                                                 */
/*  D. Turner, R.Wilhelm, and W. Lemberg                                    */
/*                                                                          */
/*  ftstring : Making string text from individual glyph information.        */
/*             This program makes use of FreeType's kerning extension       */
/*                                                                          */
/*  Keys:                                                                   */
/*                                                                          */
/*  x :   fine counter-clockwise rotation                                   */
/*  c :   fine clockwise rotation                                           */
/*                                                                          */
/*  v :   fast counter-clockwise rotation                                   */
/*  b :   fast clockwise rotation                                           */
/*                                                                          */
/*  + :   fast scale up                                                     */
/*  - :   fast scale down                                                   */
/*  u :   fine scale down                                                   */
/*  j :   fine scale up                                                     */
/*                                                                          */
/*  l :   go to next glyph                                                  */
/*  k :   go to previous glyph                                              */
/*                                                                          */
/*  o :   go to tenth next glyph                                            */
/*  i :   go to tenth previous glyph                                        */
/*                                                                          */
/*  0 :   go to hundredth next glyph                                        */
/*  9 :   go to hundredth previous glyph                                    */
/*                                                                          */
/*  ) :   go to 1000th next glyph                                           */
/*  ( :   go to 1000th previous glyph                                       */
/*                                                                          */
/*  } :   go to 10000th next glyph                                          */
/*  { :   go to 10000th previous glyph                                      */
/*                                                                          */
/*  n :   go to next (or last) .ttf file                                    */
/*  p :   go to previous (or first) .ttf file                               */
/*                                                                          */
/*  h :   toggle hinting                                                    */
/*                                                                          */
/*  ESC :   exit                                                            */
/*                                                                          */
/*                                                                          */
/*  NOTE:  This is just a test program that is used to show off and         */
/*         debug the current engine.                                        */
/*                                                                          */
/****************************************************************************/

#ifdef ARM
#include "std.h"
#include "graflink.h"
#endif

#include "freetype.h"
#include "ftxkern.h"
#include "tterror.h"  /* for Panic() and Message() only */

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

#include "gmain.h"
#include "gevents.h"
#include "gdriver.h"
#include "display.h"

#ifdef ARM
#include "armsup.c" /* pull in our routines */
#endif

#include "common.h"

#define  Pi         3.1415926535

#define  MAXPTSIZE  500                 /* dtp */
#define  Center_X   ( Bit.width / 2 )   /* dtp */
#define  Center_Y   ( Bit.rows  / 2 )   /* dtp */

  char                Header[128];

  TT_Engine    engine;
  TT_Face      face;
  TT_Instance  instance;
  TT_Glyph     glyph;
  TT_CharMap   char_map;

  TT_Glyph_Metrics     metrics;
  TT_Outline           outline;
  TT_Face_Properties   properties;
  TT_Instance_Metrics  imetrics;

  int  num_glyphs;

  int  ptsize;
  int  hinted;

  int            Rotation;
  int            Fail;
  int            Num;
  unsigned char  autorun;

  int            gray_render;

  short  glyph_code[128];
  int    num_codes;

/* Convert an ASCII string to its Unicode */
  static void CharToUnicode( char* source )
  {
    int i, n;
    short platform, encoding;

    /* First, look for a Unicode charmap */

    n = TT_Get_CharMap_Count( face );

    for ( i = 0; i < n; i++ )
    {
      TT_Get_CharMap_ID( face, i, &platform, &encoding );
      if ( (platform == 3 && encoding == 1 )  ||
           (platform == 0 && encoding == 0 ) )
      {
        TT_Get_CharMap( face, i, &char_map );
        i     = n+1;
      }
    }

    if ( i == n )
      Panic( "Sorry, but this font doesn't contain any Unicode mapping table\n" );

    for ( n = 0; n < 128 && source[n]; n++ )
      glyph_code[n] = TT_Char_Index( char_map, (short)source[n] );

    num_codes = n;
  }

  static TT_Error  Reset_Scale( int  pointSize )
  {
    TT_Error  error;

    if ( (error = TT_Set_Instance_CharSize( instance, pointSize*64 )) )
    {
      RestoreScreen();
      Message( "error = %d\n", error );
      Panic( "could not reset instance\n" );
    }
    TT_Get_Instance_Metrics( instance, &imetrics );

    /* now re-allocates the small bitmap */
    if ( gray_render )
    {
      Init_Small( imetrics.x_ppem, imetrics.y_ppem );
      Clear_Small();
    }

    return TT_Err_Ok;
  }


  static TT_Error  LoadTrueTypeChar( int  idx, int  hint )
  {
    int  flags;

    flags = TTLOAD_SCALE_GLYPH;
    if ( hint )
      flags |= TTLOAD_HINT_GLYPH;

    return TT_Load_Glyph( instance, glyph, idx, flags );
  }


  static TT_Error  Render_All()
  {
    TT_F26Dot6  x, y, z, minx, miny, maxx, maxy;
    int         i;

    TT_Error    error;

    /* On the first pass, we compute the compound bounding box */

    x = y = 0;

    minx = miny = maxx = maxy = 0;

    for ( i=0; i < num_codes; i++ )
    {
      if ( !(error = LoadTrueTypeChar( glyph_code[i], hinted )) )
      {
        TT_Get_Glyph_Metrics( glyph, &metrics );

        z = x + metrics.bbox.xMin;
        if ( minx > z )
          minx = z;

        z = x + metrics.bbox.yMax;
        if ( maxx < z )
          maxx = z;

        z = y + metrics.bbox.yMin;
        if ( miny > z )
          miny = z;

        z = y + metrics.bbox.yMax;
        if ( maxy < z )
          maxy = z;

        x += metrics.advance & -64;
      }
      else
        Fail++;
    }

    /* We now center the bbox inside the target bitmap */

    minx = ( minx & -64 ) >> 6;
    miny = ( miny & -64 ) >> 6;

    maxx = ( (maxx+63) & -64 ) >> 6;
    maxy = ( (maxy+63) & -64 ) >> 6;

    maxx -= minx;
    maxy -= miny;

    minx = (Bit.width - maxx)/2;
    miny = (Bit.rows  + miny)/2;

    maxx += minx;
    maxy += maxy;

    /* On the second pass, we render each glyph to its centered position */
    /* This is slow, because we reload each glyph to render it !         */

    x = minx;
    y = miny;

    for ( i = 0; i < num_codes; i++ )
    {
      if ( !(error = LoadTrueTypeChar( glyph_code[i], hinted )) )
      {
        TT_Get_Glyph_Metrics( glyph, &metrics );

        Render_Single_Glyph( gray_render, glyph, x, y );

        x += metrics.advance/64;
      }
    }

    return TT_Err_Ok;
  }


  static int  Process_Event( TEvent*  event )
  {
    switch ( event->what )
    {
    case event_Quit:            /* ESC or q */
      return 0;
      
    case event_Keyboard:
      if ( event->info == 'n' ) /* Next file */
        return 'n';
      if ( event->info == 'p' ) /* Previous file */
        return 'p';
      if ( event->info == 'h' ) /* Toggle hinting */
        hinted = !hinted;
      break;
      
    case event_Rotate_Glyph:
      Rotation = ( Rotation + event->info ) & 1023;
      break;

    case event_Scale_Glyph:
      ptsize += event->info;
      if ( ptsize < 1 )         ptsize = 1;
      if ( ptsize > MAXPTSIZE ) ptsize = MAXPTSIZE;
      break;

    case event_Change_Glyph:
      Num += event->info;
      if ( Num < 0 )           Num = 0;
      if ( Num >= num_glyphs ) Num = num_glyphs - 1;
      break;
    }

    return 1;
  }


  static void  usage( char*  execname )
  {
    Message( "\n" );
    Message( "ftstring: simple String Test Display - part of the FreeType project\n" );
    Message( "-------------------------------------------------------------------\n" );
    Message( "\n" );
    Message( "Usage: %s [options below] ppem fontname[.ttf|.ttc] string\n",
             execname );
    Message( "\n" );
    Message( "  -g     gray-level rendering (default: none)\n" );
    Message( "\n" );

    exit( 1 );
  }


  int  main( int  argc, char**  argv ) 
  {
    int    i, old_ptsize, orig_ptsize, file;
    int    XisSetup = 0;
    char   filename[128 + 4];
    char   alt_filename[128 + 4];
    char*  execname;
    int    option;

    TT_Error  error;
    TEvent    event;
 
    execname = argv[0];

    while ( 1 )
    {
      option = getopt( argc, argv, "gf:r:" );

      if ( option == -1 )
        break;

      switch ( option )
      {
      case 'g':
        gray_render = 1;
        break;

      default:
        usage( execname );
        break;
      }
    }

    argc -= optind;
    argv += optind;

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

    if ( sscanf( argv[0], "%d", &orig_ptsize ) != 1 )
      orig_ptsize = 64;

    file = 1;

    /* Initialize engine */

    if ( (error = TT_Init_FreeType( &engine )) )
      Panic( "ERROR: While initializing engine, code = %d\n", error );

  NewFile:
    ptsize = orig_ptsize;
    hinted = 1;

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

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

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

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

    /* Load face */

    error = TT_Open_Face( engine, filename, &face );

    if ( error == TT_Err_Could_Not_Open_File )
    {
      strcpy( filename, alt_filename );
      error = TT_Open_Face( engine, alt_filename, &face );
    }

    if ( error == TT_Err_Could_Not_Open_File )
      Panic( "ERROR: could not find/open %s\n", filename );
    else if (error)
      Panic( "ERROR: while opening %s, error code = %x\n", filename, error );

    /* get face properties and allocate preload arrays */

    TT_Get_Face_Properties( face, &properties );

    num_glyphs = properties.num_Glyphs;
        
    /* create glyph */

    error = TT_New_Glyph( face, &glyph );
    if ( error )
      Panic( "ERROR: could not create glyph container\n" );

    /* create instance */

    error = TT_New_Instance( face, &instance );
    if ( error )
      Panic( "ERROR: could not create instance for %s\n", filename );

    error = TT_Set_Instance_Resolutions( instance, 96, 96 );
    if ( error )
      Panic( "ERROR: could not set device resolutions" );

    if ( !XisSetup )
    {
      XisSetup = 1;

      if ( gray_render ) 
      {
        if ( !SetGraphScreen( Graphics_Mode_Gray ) )
          Panic( "ERROR: could not set up grayscale graphics mode\n" );

        TT_Set_Raster_Gray_Palette( engine, virtual_palette );
      }
      else
      {
        if ( !SetGraphScreen( Graphics_Mode_Mono ) )
          Panic( "ERROR: could not set up mono graphics mode\n" );
      }
    }
  
    Init_Display( gray_render );

    Reset_Scale( ptsize );

    old_ptsize = ptsize;

    Fail = 0;
    Num  = 0;

    CharToUnicode( ( argv[2] ? argv[2] :
                     "The quick brown fox jumps over the lazy dog" ) );

    for ( ;; )
    {
      int  key;

      Clear_Display();
      Render_All();
      if (gray_render)
	Convert_To_Display_Palette();

      sprintf( Header, "%s:   Glyph: %4d  ptsize: %4d  hinting: %s",
                       basename( filename ), Num, ptsize,
                       hinted ? "on" : "off" );
#ifndef X11
#ifndef OS2
      Print_XY( 0, 0, Header );
#endif
#endif

      Display_Bitmap_On_Screen( Bit.bitmap, Bit.rows, Bit.cols );

      Get_Event( &event );
      if ( !( key = Process_Event( &event ) ) ) goto Fin;

      if ( key == 'n' )
      {
        TT_Close_Face( face );

        if ( file < argc - 1 )
          file++;

        goto NewFile;
      }

      if ( key == 'p' )
      {
        TT_Close_Face( face );

        if ( file > 1 )
          file--;

        goto NewFile;
      }

      if ( ptsize != old_ptsize )
      {
        if ( Reset_Scale( ptsize ) )
          Panic( "ERROR: could not resize font\n" );

        old_ptsize = ptsize;
      }
    }

  Fin:
    RestoreScreen();

    TT_Done_FreeType( engine );

    Message( "execution completed succesfully\n" );
    Message( "fails = %d\n", Fail );

    return 0;
}


/* End */
