ref: b5c1a4e5673a7e80abc8b59d7bf87a9b123f5f2a
parent: 6b0149b826bc78d7de29501f361caa022ff5ea3f
author: David Turner <[email protected]>
date: Tue Mar 28 06:15:37 EST 2000
a new program to demonstrate the new convenience glyph API (see include/ftglyph.h). Supports kerning, rotation, sub-pixel rendering.. Could be easily modified to reach the level of strtto when we have the relevant OpenType module handy..
--- /dev/null
+++ b/demos/src/ftstring.c
@@ -1,0 +1,835 @@
+/****************************************************************************/
+/* */
+/* 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.h"
+#include "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"
+
+#include "ftgrays.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 unsigned 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 = { 127 };
+
+ static int graph_init = 0;
+ static int render_mode = 1;
+ static int use_grays = 0;
+
+ /* the standard raster's interface */
+ static FT_Raster_Funcs std_raster;
+
+ 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 = 128;
+
+ 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 = 128;
+ 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 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" );
+ grWriteln(" g : toggle between 'smooth' and 'standard' anti-aliaser" );
+ 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 void reset_raster( void )
+ {
+ FT_Error error;
+
+ error = 1;
+ if ( use_grays && antialias )
+ error = FT_Set_Raster( library, &ft_grays_raster );
+
+ if (error)
+ (void)FT_Set_Raster( library, &std_raster );
+ }
+
+
+ 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" );
+ reset_raster();
+ 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('g'):
+ use_grays = !use_grays;
+ new_header = ( use_grays
+ ? "now using the smooth anti-aliaser"
+ : "now using the standard anti-aliaser" );
+ reset_raster();
+ break;
+
+ 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" );
+
+ /* retrieve the standard raster's interface */
+ (void)FT_Get_Raster( library, ft_glyph_format_outline, &std_raster );
+
+ 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( 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 */
+