shithub: freetype+ttf2subf

ref: 60402683ba9b64de99c5c31d029eb3b564404d97
dir: /demos/src/ftstring.c/

View raw version
/****************************************************************************/
/*                                                                          */
/*  The FreeType project -- a free and portable quality TrueType renderer.  */
/*                                                                          */
/*  Copyright 1996-1999 by                                                  */
/*  D. Turner, R.Wilhelm, and W. Lemberg                                    */
/*                                                                          */
/*                                                                          */
/*  FTString.c - simple text string display                                 */
/*                                                                          */
/****************************************************************************/

#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include "common.h"

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

#include "graph.h"
#include "grfont.h"

#define  DIM_X   500
#define  DIM_Y   400

#define  CENTER_X   (bit.width/2)
#define  CENTER_Y   (bit.rows/2)

#define  MAXPTSIZE  500                 /* dtp */

  static char  Header[128];
  static char* new_header = 0;

  static char*  Text = "The quick brown fox jumps over the lazy dog";

  static FT_Library    library;      /* the FreeType library            */
  static FT_Face       face;         /* the font face                   */
  static FT_Error      error;        /* error returned by FreeType ?    */

  static grSurface*     surface;     /* current display surface         */
  static grBitmap       bit;         /* current display bitmap          */

  static int  ptsize;                /* current point size */
  static int  Num;
  static int  Rotation = 0;
  static int  Fail;

  static int  hinted    = 1;       /* is glyph hinting active ?    */
  static int  antialias = 1;       /* is anti-aliasing active ?    */
  static int  use_sbits = 1;       /* do we use embedded bitmaps ? */
  static int  kerning   = 1;

  static int  res = 72;  /* default resolution in dpi */

  static grColor  fore_color = { 255 };

  static int  graph_init  = 0;
  static int  render_mode = 1;

  static FT_Matrix      trans_matrix;
  static int            transform = 0;

  static FT_Vector      string_center;

  typedef struct TGlyph_
  {
    FT_UInt    glyph_index;    /* glyph index in face      */
    FT_Vector  pos;            /* position of glyph origin */
    FT_Glyph   image;          /* glyph image              */

  } TGlyph, *PGlyph;

#define FLOOR(x)  ((x) & -64)
#define CEIL(x)   (((x)+63) & -64)
#define TRUNC(x)  ((x) >> 6)



/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****                                                                    ****/
/****    U T I L I T Y   F U N C T I O N S                               ****/
/****                                                                    ****/
/****                                                                    ****/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/

#define DEBUGxxx

#ifdef DEBUG
#define LOG(x)  LogMessage##x
#else
#define LOG(x)  /* rien */
#endif

#ifdef DEBUG
  static void  LogMessage( const char*  fmt, ... )
  {
    va_list  ap;

    va_start( ap, fmt );
    vfprintf( stderr, fmt, ap );
    va_end( ap );
  }
#endif

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


/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****                                                                    ****/
/****    D I S P L A Y   M A N A G E M E N T                             ****/
/****                                                                    ****/
/****                                                                    ****/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/

#define MAX_GLYPHS 512

 /***********************************************************************
  *
  *  The following arrays are used to store the glyph set that makes
  *  up a string of text..
  *
  */
  static TGlyph  glyphs[ MAX_GLYPHS ];
  static int     num_glyphs;


 /**************************************************************
  *
  *  Initialise the display surface
  *
  */
  static int  init_display( void )
  {
    grInitDevices();

    bit.mode   = gr_pixel_mode_gray;
    bit.width  = DIM_X;
    bit.rows   = DIM_Y;
    bit.grays  = 256;

    surface = grNewSurface( 0, &bit );
    if (!surface)
      PanicZ( "could not allocate display surface\n" );

    graph_init = 1;
    return 0;
  }

 /**************************************************************
  *
  *  Clears the display surface
  *
  */
  static void  clear_display( void )
  {
    long  size = (long)bit.pitch * bit.rows;

    if (size < 0) size = -size;
    memset( bit.buffer, 0, size );
  }


  static FT_Error  reset_scale( int  pointSize )
  {
    FT_Error  error;

    error = FT_Set_Char_Size( face, pointSize << 6,
                                    pointSize << 6,
                                    res,
                                    res );
    return FT_Err_Ok;
  }


 /**************************************************************
  *
  *  Compute the dimension of a string of glyphs in pixels
  *
  */
  static void  compute_bbox( FT_BBox*  abbox )
  {
    PGlyph  glyph = glyphs;
    FT_BBox bbox;
    int     n;

    bbox.xMin = 32000; bbox.xMax = -32000;
    bbox.yMin = 32000; bbox.yMax = -32000;

    for ( n = 0; n < num_glyphs; n++, glyph++ )
    {
      FT_BBox  cbox;
      FT_Pos   x, y;

      if (!glyph->image) continue;

      x = glyph->pos.x >> 6;
      y = glyph->pos.y >> 6;

      FT_Glyph_Get_Box( glyph->image, &cbox );

      cbox.xMin += x;
      cbox.yMin += y;
      cbox.xMax += x;
      cbox.yMax += y;

      if (cbox.xMin < bbox.xMin) bbox.xMin = cbox.xMin;
      if (cbox.xMax > bbox.xMax) bbox.xMax = cbox.xMax;
      if (cbox.yMin < bbox.yMin) bbox.yMin = cbox.yMin;
      if (cbox.yMax > bbox.yMax) bbox.yMax = cbox.yMax;
    }
    *abbox = bbox;
  }


 /**************************************************************
  *
  *  Layout a string of glyphs
  *
  */
  static void  layout_glyphs( void )
  {
    PGlyph    glyph = glyphs;
    FT_Error  error;
    int       n;
    FT_Vector origin;
    FT_Pos    origin_x = 0;
    FT_UInt   load_flags;
    FT_UInt   num_grays;
    FT_UInt   prev_index = 0;

    load_flags = FT_LOAD_DEFAULT;
    if( !hinted )
      load_flags |= FT_LOAD_NO_HINTING;

    num_grays = 256;
    if (!antialias)
      num_grays = 0;

    for ( n = 0; n < num_glyphs; n++, glyph++ )
    {
      /* compute glyph origin */
      if (kerning)
      {
        if (prev_index)
        {
          FT_Vector  kern;

          FT_Get_Kerning( face, prev_index, glyph->glyph_index, &kern );
          kern.x = FT_MulFix( kern.x, face->size->metrics.x_scale );
          if (hinted) kern.x = (kern.x+32) & -64;

          origin_x += kern.x;
        }
        prev_index = glyph->glyph_index;
      }

      origin.x = origin_x;
      origin.y = 0;

      if (transform)
        FT_Vector_Transform( &origin, &trans_matrix );

      /* clear existing image if there is one */
      if (glyph->image)
        FT_Done_Glyph(glyph->image);

      /* load the glyph image                       */
      /* for now, we take a monochrome glyph bitmap */
      error = FT_Get_Glyph_Bitmap( face, glyph->glyph_index,
                                   load_flags,
                                   num_grays,
                                   &origin,
                                   (FT_BitmapGlyph*)&glyph->image );
      if (error) continue;

      glyph->pos = origin;

      origin_x  += glyph->image->advance;
    }
    string_center.x = origin_x / 2;
    string_center.y = 0;
    if (transform)
      FT_Vector_Transform( &string_center, &trans_matrix );
  }

 /**************************************************************
  *
  *  Renders a given glyph vector set
  *
  */
  static  void  render_string( FT_Pos  x, FT_Pos  y )
  {
    PGlyph    glyph = glyphs;
    grBitmap  bit3;
    int       n;

    for ( n = 0; n < num_glyphs; n++, glyph++ )
    {
      if (!glyph->image)
        continue;

      switch (glyph->image->glyph_type)
      {
        case ft_glyph_type_bitmap:
          {
            /* this is a bitmap, we simply blit it to our target surface */
            FT_BitmapGlyph  bitm   = (FT_BitmapGlyph)glyph->image;
            FT_Bitmap*      source = &bitm->bitmap;
            FT_Pos          x_top, y_top;

            bit3.rows   = source->rows;
            bit3.width  = source->width;
            bit3.pitch  = source->pitch;
            bit3.buffer = source->buffer;

            switch (source->pixel_mode)
            {
              case ft_pixel_mode_mono:
                bit3.mode  = gr_pixel_mode_mono;
                break;

              case ft_pixel_mode_grays:
                bit3.mode  = gr_pixel_mode_gray;
                bit3.grays = source->num_grays;
                break;

              default:
                continue;
            }

            /* now render the bitmap into the display surface */
            x_top = x + (glyph->pos.x >> 6) + bitm->left;
            y_top = y - (glyph->pos.y >> 6) - bitm->top;
            grBlitGlyphToBitmap( &bit, &bit3, x_top, y_top, fore_color );
          }
          break;
#if 0
        case ft_glyph_type_outline:
          {
            /* in the case of outlines, we directly render it into the */
            /* target surface with the smooth renderer..               */
            FT_OutlineGlyph  out = (FT_OutlineGlyph)glyph->image;

            FT_Outline_Translate( (x+pen_pos[n]) << 6, (y+
            error = FT_Outline_Render(
          }
          break;
#endif
        default:
          ;
      }
    }
  }


 /**************************************************************
  *
  *  Convert a string of text into a glyph vector
  *
  *  XXX: For now, we perform a trivial conversion
  *
  */
  static  void  prepare_text( const unsigned char*  string )
  {
    const unsigned char*  p     = (const unsigned char*)string;
    PGlyph                glyph = glyphs;
    FT_UInt               glyph_index;

    num_glyphs = 0;
    while (*p)
    {
      glyph_index = FT_Get_Char_Index( face, (FT_ULong)*p );
      glyph->glyph_index = glyph_index;
      glyph++;
      num_glyphs++;
      if (num_glyphs >= MAX_GLYPHS)
        break;
      p++;
    }
  }


  static void  reset_transform( void )
  {
    double    angle   = Rotation*3.14159/64.0;
    FT_Fixed  cosinus = (FT_Fixed)(cos(angle)*65536.0);
    FT_Fixed  sinus   = (FT_Fixed)(sin(angle)*65536.0);

    transform       = (angle != 0);
    trans_matrix.xx = cosinus;
    trans_matrix.xy = -sinus;
    trans_matrix.yx = sinus;
    trans_matrix.yy = cosinus;

    FT_Set_Transform(face,&trans_matrix, 0);
  }

/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****                                                                    ****/
/****    E V E N T   H A N D L I N G                                     ****/
/****                                                                    ****/
/****                                                                    ****/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/


  static void Help( )
  {
    grEvent  dummy_event;

    clear_display();
    grGotoxy( 0, 0 );
    grSetMargin( 2, 1 );
    grGotobitmap( &bit );

    grWriteln("FreeType String Viewer - part of the FreeType test suite" );
    grLn();
    grWriteln("This program is used to display a string of text using" );
    grWriteln("the new convenience API of the FreeType 2 library.");
    grLn();
    grWriteln("Use the following keys :");
    grLn();
    grWriteln("  F1 or ?   : display this help screen" );
    grWriteln("  a         : toggle anti-aliasing" );
    grWriteln("  h         : toggle outline hinting" );
    grWriteln("  k         : toggle kerning" );
    grLn();
    grWriteln("  Up        : increase pointsize by 1 unit" );
    grWriteln("  Down      : decrease pointsize by 1 unit" );
    grWriteln("  Page Up   : increase pointsize by 10 units" );
    grWriteln("  Page Down : decrease pointsize by 10 units" );
    grLn();
    grWriteln("  Right     : rotate counter-clockwise" );
    grWriteln("  Left      : rotate clockwise" );
    grWriteln("  F7        : big rotate counter-clockwise");
    grWriteln("  F8        : big rotate clockwise");
    grLn();
    grWriteln("press any key to exit this help screen");

    grRefreshSurface( surface );
    grListenSurface( surface, gr_event_key, &dummy_event );
  }


  static int  Process_Event( grEvent*  event )
  {
    int  i;

    switch ( event->key )
    {
    case grKeyEsc:            /* ESC or q */
    case grKEY('q'):
      return 0;

    case grKEY('k'):
      kerning = !kerning;
      new_header = ( kerning
                   ? "kerning is now active"
                   : "kerning is now ignored" );
      return 1;

    case grKEY('a'):
      antialias = !antialias;
      new_header = ( antialias
                   ? "anti-aliasing is now on"
                   : "anti-aliasing is now off" );
      return 1;

    case grKEY('b'):
      use_sbits  = !use_sbits;
      new_header = ( use_sbits
                   ? "embedded bitmaps are now used when available"
                   : "embedded bitmaps are now ignored" );
      return 1;

    case grKEY('n'):
    case grKEY('p'):
      return (int)event->key;

    case grKEY('h'):
      hinted = !hinted;
      new_header = ( hinted
                   ? "glyph hinting is now active"
                   : "glyph hinting is now ignored" );
      break;

    case grKEY(' '):
      render_mode ^= 1;
      new_header = ( render_mode
                   ? "rendering all glyphs in font"
                   : "rendering test text string" );
      break;

    case grKeyF1:
    case grKEY('?'):
      Help();
      return 1;

#if 0
    case grKeyF3:  i =  16; goto Do_Rotate;
    case grKeyF4:  i = -16; goto Do_Rotate;
    case grKeyF5:  i =   1; goto Do_Rotate;
    case grKeyF6:  i =  -1; goto Do_Rotate;
#endif

    case grKeyPageUp:   i =  10; goto Do_Scale;
    case grKeyPageDown: i = -10; goto Do_Scale;
    case grKeyUp:       i =   1; goto Do_Scale;
    case grKeyDown:     i =  -1; goto Do_Scale;

    case grKeyLeft:  i =  -1; goto Do_Rotate;
    case grKeyRight: i =   1; goto Do_Rotate;
    case grKeyF7:    i = -10; goto Do_Rotate;
    case grKeyF8:    i =  10; goto Do_Rotate;
    default:
      ;
    }
    return 1;

  Do_Rotate:
    Rotation = (Rotation + i) & 127;
    return 1;

  Do_Scale:
    ptsize += i;
    if (ptsize < 1)         ptsize = 1;
    if (ptsize > MAXPTSIZE) ptsize = MAXPTSIZE;
    return 1;

#if 0
  Do_Glyph:
    Num += i;
    if (Num < 0)           Num = 0;
    if (Num >= num_glyphs) Num = num_glyphs-1;
    return 1;
#endif
  }


/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****                                                                    ****/
/****    M A I N   P R O G R A M                                         ****/
/****                                                                    ****/
/****                                                                    ****/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/


  static void  usage( char*  execname )
  {
    fprintf( stderr,  "\n" );
    fprintf( stderr,  "ftview: simple string viewer -- part of the FreeType project\n" );
    fprintf( stderr,  "------------------------------------------------------------\n" );
    fprintf( stderr,  "\n" );
    fprintf( stderr,  "Usage: %s [options below] ppem fontname[.ttf|.ttc] ...\n",
             execname );
    fprintf( stderr,  "\n" );
    fprintf( stderr,  "  -r R        use resolution R dpi (default: 72 dpi)\n" );
    fprintf( stderr,  "  -m message  message to display\n" );
    fprintf( stderr,  "\n" );

    exit( 1 );
  }


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

    FT_Error  error;
    grEvent   event;

    execname = ft_basename( argv[0] );

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

      if ( option == -1 )
        break;

      switch ( option )
      {
      case 'r':
        res = atoi( optarg );
        if ( res < 1 )
          usage( execname );
        break;

      case 'm':
        if (argc < 3)
          usage( execname );
        Text = optarg;
        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 */
    error = FT_Init_FreeType( &library );
    if (error) PanicZ( "Could not initialise FreeType library" );

  NewFile:
    ptsize      = orig_ptsize;
    hinted      = 1;
    file_loaded = 0;

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

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

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

#ifndef macintosh
    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) goto Display_Font;

    /* prepare the text to be rendered */
    prepare_text( (unsigned char*)Text );

    file_loaded++;

    error = reset_scale( ptsize );
    if (error) goto Display_Font;

  Display_Font:
    /* initialise graphics if needed */
    if ( !XisSetup )
    {
      XisSetup = 1;
      init_display();
    }

    grSetTitle( surface, "FreeType String Viewer - press F1 for help" );
    old_ptsize = ptsize;

    if ( file_loaded >= 1 )
    {
      Fail = 0;
      Num  = first_glyph;

      if ( Num >= num_glyphs )
        Num = num_glyphs-1;

      if ( Num < 0 )
        Num = 0;
    }

    for ( ;; )
    {
      int  key;

      clear_display();

      if ( file_loaded >= 1 )
      {
        /* layout & render string */
        {
          FT_BBox  bbox;

          reset_transform();
          layout_glyphs();
          compute_bbox( &bbox );
          render_string( (bit.width-(string_center.x >> 5))/2,
                         (bit.rows +(string_center.y >> 5))/2 );
        }

        sprintf( Header, "%s %s (file %s)",
                         face->family_name,
                         face->style_name,
                         ft_basename( filename ) );

        if (!new_header)
          new_header = Header;

        grWriteCellString( &bit, 0, 0, new_header, fore_color );
        new_header = 0;

        sprintf( Header, "at %d points, rotation = %d",
                         ptsize,
                         Rotation );
      }
      else
      {
        sprintf( Header, "%s : is not a font file or could not be opened",
                         ft_basename(filename) );
      }

      grWriteCellString( &bit, 0, 8, Header, fore_color );
      grRefreshSurface( surface );

      grListenSurface( surface, 0, &event );
      if ( !( key = Process_Event( &event ) ) )
        goto Fin;

      if ( key == 'n' )
      {
        if (file_loaded >= 1)
          FT_Done_Face( face );

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

        goto NewFile;
      }

      if ( key == 'p' )
      {
        if (file_loaded >= 1)
          FT_Done_Face( face );

        if ( file > 1 )
          file--;

        goto NewFile;
      }

      if ( ptsize != old_ptsize )
      {
        if ( reset_scale( ptsize ) )
          PanicZ( "Could not resize font." );

        old_ptsize = ptsize;
      }
    }

  Fin:
#if 0
    grDoneSurface(surface);
    grDone();
#endif
    printf( "Execution completed successfully.\n" );
    printf( "Fails = %d\n", Fail );

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


/* End */