ftrandom.c

Go to the documentation of this file.
00001 /* Copyright (C) 2005, 2007, 2008 by George Williams */
00002 /*
00003  * Redistribution and use in source and binary forms, with or without
00004  * modification, are permitted provided that the following conditions are met:
00005 
00006  * Redistributions of source code must retain the above copyright notice, this
00007  * list of conditions and the following disclaimer.
00008 
00009  * Redistributions in binary form must reproduce the above copyright notice,
00010  * this list of conditions and the following disclaimer in the documentation
00011  * and/or other materials provided with the distribution.
00012 
00013  * The name of the author may not be used to endorse or promote products
00014  * derived from this software without specific prior written permission.
00015 
00016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00017  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00018  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
00019  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00020  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00021  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
00022  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00023  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00024  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00025  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  */
00027 
00028 /* modified by Werner Lemberg <wl@gnu.org>       */
00029 /* This file is now part of the FreeType library */
00030 
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <strings.h>
00036 #include <sys/types.h>
00037 #include <sys/stat.h>
00038 #include <sys/wait.h>
00039 #include <unistd.h>
00040 #include <dirent.h>
00041 #include <math.h>
00042 #include <signal.h>
00043 #include <time.h>
00044 
00045 #include <ft2build.h>
00046 #include FT_FREETYPE_H
00047 #include FT_OUTLINE_H
00048 
00049 #define true     1
00050 #define false    0
00051 #define forever  for (;;)
00052 
00053 
00054   static int    check_outlines = false;
00055   static int    nohints        = false;
00056   static int    rasterize      = false;
00057   static char*  results_dir    = "results";
00058 
00059 #define GOOD_FONTS_DIR  "/home/wl/freetype-testfonts"
00060 
00061   static char*  default_dir_list[] =
00062   {
00063     GOOD_FONTS_DIR,
00064     NULL
00065   };
00066 
00067   static char*  default_ext_list[] =
00068   {
00069     "ttf",
00070     "otf",
00071     "ttc",
00072     "cid",
00073     "pfb",
00074     "pfa",
00075     "bdf",
00076     "pcf",
00077     "pfr",
00078     "fon",
00079     "otb",
00080     "cff",
00081     NULL
00082   };
00083 
00084   static int  error_count    = 1;
00085   static int  error_fraction = 0;
00086 
00087   static FT_F26Dot6  font_size = 12 * 64;
00088 
00089   static struct fontlist
00090   {
00091     char*         name;
00092     int           len;
00093     unsigned int  isbinary: 1;
00094     unsigned int  isascii: 1;
00095     unsigned int  ishex: 1;
00096 
00097   } *fontlist;
00098 
00099   static int  fcnt;
00100 
00101 
00102   static int
00103   FT_MoveTo( const FT_Vector  *to,
00104              void             *user )
00105   {
00106     return 0;
00107   }
00108 
00109 
00110   static int
00111   FT_LineTo( const FT_Vector  *to,
00112              void             *user )
00113   {
00114     return 0;
00115   }
00116 
00117 
00118   static int
00119   FT_ConicTo( const FT_Vector  *_cp,
00120               const FT_Vector  *to,
00121               void             *user )
00122   {
00123     return 0;
00124   }
00125 
00126 
00127   static int
00128   FT_CubicTo( const FT_Vector  *cp1,
00129               const FT_Vector  *cp2,
00130               const FT_Vector  *to,
00131               void             *user )
00132   {
00133     return 0;
00134   }
00135 
00136 
00137   static FT_Outline_Funcs outlinefuncs =
00138   {
00139     FT_MoveTo,
00140     FT_LineTo,
00141     FT_ConicTo,
00142     FT_CubicTo,
00143     0, 0          /* No shift, no delta */
00144   };
00145 
00146 
00147   static void
00148   TestFace( FT_Face  face )
00149   {
00150     int  gid;
00151     int  load_flags = FT_LOAD_DEFAULT;
00152 
00153 
00154     if ( check_outlines         &&
00155          FT_IS_SCALABLE( face ) )
00156       load_flags = FT_LOAD_NO_BITMAP;
00157 
00158     if ( nohints )
00159       load_flags |= FT_LOAD_NO_HINTING;
00160 
00161     FT_Set_Char_Size( face, 0, font_size, 72, 72 );
00162 
00163     for ( gid = 0; gid < face->num_glyphs; ++gid )
00164     {
00165       if ( check_outlines         &&
00166            FT_IS_SCALABLE( face ) )
00167       {
00168         if ( !FT_Load_Glyph( face, gid, load_flags ) )
00169           FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL );
00170       }
00171       else
00172         FT_Load_Glyph( face, gid, load_flags );
00173 
00174       if ( rasterize )
00175         FT_Render_Glyph( face->glyph, ft_render_mode_normal );
00176     }
00177 
00178     FT_Done_Face( face );
00179   }
00180 
00181 
00182   static void
00183   ExecuteTest( char*  testfont )
00184   {
00185     FT_Library  context;
00186     FT_Face     face;
00187     int         i, num;
00188 
00189 
00190     if ( FT_Init_FreeType( &context ) )
00191     {
00192       fprintf( stderr, "Can't initialize FreeType.\n" );
00193       exit( 1 );
00194     }
00195 
00196     if ( FT_New_Face( context, testfont, 0, &face ) )
00197     {
00198       /* The font is erroneous, so if this fails that's ok. */
00199       exit( 0 );
00200     }
00201 
00202     if ( face->num_faces == 1 )
00203       TestFace( face );
00204     else
00205     {
00206       num = face->num_faces;
00207       FT_Done_Face( face );
00208 
00209       for ( i = 0; i < num; ++i )
00210       {
00211         if ( !FT_New_Face( context, testfont, i, &face ) )
00212           TestFace( face );
00213       }
00214     }
00215 
00216     exit( 0 );
00217   }
00218 
00219 
00220   static int
00221   extmatch( char*   filename,
00222             char**  extensions )
00223   {
00224     int    i;
00225     char*  pt;
00226 
00227 
00228     if ( extensions == NULL )
00229       return true;
00230 
00231     pt = strrchr( filename, '.' );
00232     if ( pt == NULL )
00233       return false;
00234     if ( pt < strrchr( filename, '/' ) )
00235       return false;
00236 
00237     for ( i = 0; extensions[i] != NULL; ++i )
00238       if ( strcasecmp( pt + 1, extensions[i] ) == 0 ||
00239            strcasecmp( pt,     extensions[i] ) == 0 )
00240         return true;
00241 
00242     return false;
00243   }
00244 
00245 
00246   static void
00247   figurefiletype( struct fontlist*  item )
00248   {
00249     FILE*  foo;
00250 
00251 
00252     item->isbinary = item->isascii = item->ishex = false;
00253 
00254     foo = fopen( item->name, "rb" );
00255     if ( foo != NULL )
00256     {
00257       /* Try to guess the file type from the first few characters... */
00258       int  ch1 = getc( foo );
00259       int  ch2 = getc( foo );
00260       int  ch3 = getc( foo );
00261       int  ch4 = getc( foo );
00262 
00263 
00264       fclose( foo );
00265 
00266       if ( ( ch1 == 0   && ch2 == 1   && ch3 == 0   && ch4 == 0   ) ||
00267            ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) ||
00268            ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) ||
00269            ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) )
00270       {
00271         /* ttf, otf, ttc files */
00272         item->isbinary = true;
00273       }
00274       else if ( ch1 == 0x80 && ch2 == '\01' )
00275       {
00276         /* PFB header */
00277         item->isbinary = true;
00278       }
00279       else if ( ch1 == '%' && ch2 == '!' )
00280       {
00281         /* Random PostScript */
00282         if ( strstr( item->name, ".pfa" ) != NULL ||
00283              strstr( item->name, ".PFA" ) != NULL )
00284           item->ishex = true;
00285         else
00286           item->isascii = true;
00287       }
00288       else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 )
00289       {
00290         /* Bare CFF */
00291         item->isbinary = true;
00292       }
00293       else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' )
00294       {
00295         /* BDF */
00296         item->ishex = true;
00297       }
00298       else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' )
00299       {
00300         /* PFR */
00301         item->isbinary = true;
00302       }
00303       else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) ||
00304                 ( ch1 == 'M'  && ch2 == 'Z' )                             )
00305       {
00306         /* Windows FON */
00307         item->isbinary = true;
00308       }
00309       else
00310       {
00311         fprintf( stderr,
00312                  "Can't recognize file type of `%s', assuming binary\n",
00313                  item->name );
00314         item->isbinary = true;
00315       }
00316     }
00317     else
00318     {
00319       fprintf( stderr, "Can't open `%s' for typing the file.\n",
00320                item->name );
00321       item->isbinary = true;
00322     }
00323   }
00324 
00325 
00326   static void
00327   FindFonts( char**  fontdirs,
00328              char**  extensions )
00329   {
00330     DIR*            examples;
00331     struct dirent*  ent;
00332 
00333     int             i, max;
00334     char            buffer[1025];
00335     struct stat     statb;
00336 
00337 
00338     max  = 0;
00339     fcnt = 0;
00340 
00341     for ( i = 0; fontdirs[i] != NULL; ++i )
00342     {
00343       examples = opendir( fontdirs[i] );
00344       if ( examples == NULL )
00345       {
00346         fprintf( stderr,
00347                  "Can't open example font directory `%s'\n",
00348                  fontdirs[i] );
00349         exit( 1 );
00350       }
00351 
00352       while ( ( ent = readdir( examples ) ) != NULL )
00353       {
00354         snprintf( buffer, sizeof ( buffer ),
00355                   "%s/%s", fontdirs[i], ent->d_name );
00356         if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) )
00357           continue;
00358         if ( extensions == NULL || extmatch( buffer, extensions ) )
00359         {
00360           if ( fcnt >= max )
00361           {
00362             max += 100;
00363             fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) );
00364             if ( fontlist == NULL )
00365             {
00366               fprintf( stderr, "Can't allocate memory\n" );
00367               exit( 1 );
00368             }
00369           }
00370 
00371           fontlist[fcnt].name = strdup( buffer );
00372           fontlist[fcnt].len  = statb.st_size;
00373 
00374           figurefiletype( &fontlist[fcnt] );
00375           ++fcnt;
00376         }
00377       }
00378 
00379       closedir( examples );
00380     }
00381 
00382     if ( fcnt == 0 )
00383     {
00384       fprintf( stderr, "Can't find matching font files.\n" );
00385       exit( 1 );
00386     }
00387 
00388     fontlist[fcnt].name = NULL;
00389   }
00390 
00391 
00392   static int
00393   getErrorCnt( struct fontlist*  item )
00394   {
00395     if ( error_count == 0 && error_fraction == 0 )
00396       return 0;
00397 
00398     return error_count + ceil( error_fraction * item->len );
00399   }
00400 
00401 
00402   static int
00403   getRandom( int  low,
00404              int  high )
00405   {
00406     if ( low - high < 0x10000L )
00407       return low + ( ( random() >> 8 ) % ( high + 1 - low ) );
00408 
00409     return low + ( random() % ( high + 1 - low ) );
00410   }
00411 
00412 
00413   static int
00414   copyfont( struct fontlist*  item,
00415             char*             newfont )
00416   {
00417     static char  buffer[8096];
00418     FILE         *good, *new;
00419     int          len;
00420     int          i, err_cnt;
00421 
00422 
00423     good = fopen( item->name, "r" );
00424     if ( good == NULL )
00425     {
00426       fprintf( stderr, "Can't open `%s'\n", item->name );
00427       return false;
00428     }
00429 
00430     new = fopen( newfont, "w+" );
00431     if ( new == NULL )
00432     {
00433       fprintf( stderr, "Can't create temporary output file `%s'\n",
00434                newfont );
00435       exit( 1 );
00436     }
00437 
00438     while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 )
00439       fwrite( buffer, 1, len, new );
00440 
00441     fclose( good );
00442 
00443     err_cnt = getErrorCnt( item );
00444     for ( i = 0; i < err_cnt; ++i )
00445     {
00446       fseek( new, getRandom( 0, item->len - 1 ), SEEK_SET );
00447 
00448       if ( item->isbinary )
00449         putc( getRandom( 0, 0xff ), new );
00450       else if ( item->isascii )
00451         putc( getRandom( 0x20, 0x7e ), new );
00452       else
00453       {
00454         int  hex = getRandom( 0, 15 );
00455 
00456 
00457         if ( hex < 10 )
00458           hex += '0';
00459         else
00460           hex += 'A' - 10;
00461 
00462         putc( hex, new );
00463       }
00464     }
00465 
00466     if ( ferror( new ) )
00467     {
00468       fclose( new );
00469       unlink( newfont );
00470       return false;
00471     }
00472 
00473     fclose( new );
00474 
00475     return true;
00476   }
00477 
00478 
00479   static int  child_pid;
00480 
00481   static void
00482   abort_test( int  sig )
00483   {
00484     /* If a time-out happens, then kill the child */
00485     kill( child_pid, SIGFPE );
00486     write( 2, "Timeout... ", 11 );
00487   }
00488 
00489 
00490   static void
00491   do_test( void )
00492   {
00493     int         i        = getRandom( 0, fcnt - 1 );
00494     static int  test_num = 0;
00495     char        buffer[1024];
00496 
00497 
00498     sprintf( buffer, "%s/test%d", results_dir, test_num++ );
00499 
00500     if ( copyfont ( &fontlist[i], buffer ) )
00501     {
00502       signal( SIGALRM, abort_test );
00503       /* Anything that takes more than 20 seconds */
00504       /* to parse and/or rasterize is an error.   */
00505       alarm( 20 );
00506       if ( ( child_pid = fork() ) == 0 )
00507         ExecuteTest( buffer );
00508       else if ( child_pid != -1 )
00509       {
00510         int  status;
00511 
00512 
00513         waitpid( child_pid, &status, 0 );
00514         alarm( 0 );
00515         if ( WIFSIGNALED ( status ) )
00516           printf( "Error found in file `%s'\n", buffer );
00517         else
00518           unlink( buffer );
00519       }
00520       else
00521       {
00522         fprintf( stderr, "Can't fork test case.\n" );
00523         exit( 1 );
00524       }
00525       alarm( 0 );
00526     }
00527   }
00528 
00529 
00530   static void
00531   usage( FILE*  out,
00532          char*  name )
00533   {
00534     fprintf( out, "%s [options] -- Generate random erroneous fonts\n"
00535                   "  and attempt to parse them with FreeType.\n\n", name );
00536 
00537     fprintf( out, "  --all                    All non-directory files are assumed to be fonts.\n" );
00538     fprintf( out, "  --check-outlines         Make sure we can parse the outlines of each glyph.\n" );
00539     fprintf( out, "  --dir <path>             Append <path> to list of font search directories.\n" );
00540     fprintf( out, "  --error-count <cnt>      Introduce <cnt> single byte errors into each font.\n" );
00541     fprintf( out, "  --error-fraction <frac>  Introduce <frac>*filesize single byte errors\n"
00542                   "                           into each font.\n" );
00543     fprintf( out, "  --ext <ext>              Add <ext> to list of extensions indicating fonts.\n" );
00544     fprintf( out, "  --help                   Print this.\n" );
00545     fprintf( out, "  --nohints                Turn off hinting.\n" );
00546     fprintf( out, "  --rasterize              Attempt to rasterize each glyph.\n" );
00547     fprintf( out, "  --results <dir>          Directory in which to place the test fonts.\n" );
00548     fprintf( out, "  --size <float>           Use the given font size for the tests.\n" );
00549     fprintf( out, "  --test <file>            Run a single test on an already existing file.\n" );
00550   }
00551 
00552 
00553   int
00554   main( int     argc,
00555         char**  argv )
00556   {
00557     char    **dirs, **exts;
00558     char    *pt, *end;
00559     int     dcnt = 0, ecnt = 0, rset = false, allexts = false;
00560     int     i;
00561     time_t  now;
00562     char*   testfile = NULL;
00563 
00564 
00565     dirs = calloc( argc + 1, sizeof ( char ** ) );
00566     exts = calloc( argc + 1, sizeof ( char ** ) );
00567 
00568     for ( i = 1; i < argc; ++i )
00569     {
00570       pt = argv[i];
00571       if ( pt[0] == '-' && pt[1] == '-' )
00572         ++pt;
00573 
00574       if ( strcmp( pt, "-all" ) == 0 )
00575         allexts = true;
00576       else if ( strcmp( pt, "-check-outlines" ) == 0 )
00577         check_outlines = true;
00578       else if ( strcmp( pt, "-dir" ) == 0 )
00579         dirs[dcnt++] = argv[++i];
00580       else if ( strcmp( pt, "-error-count" ) == 0 )
00581       {
00582         if ( !rset )
00583           error_fraction = 0;
00584         rset = true;
00585         error_count = strtol( argv[++i], &end, 10 );
00586         if ( *end != '\0' )
00587         {
00588           fprintf( stderr, "Bad value for error-count: %s\n", argv[i] );
00589           exit( 1 );
00590         }
00591       }
00592       else if ( strcmp( pt, "-error-fraction" ) == 0 )
00593       {
00594         if ( !rset )
00595           error_count = 0;
00596         rset = true;
00597         error_fraction = strtod( argv[++i], &end );
00598         if ( *end != '\0' )
00599         {
00600           fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] );
00601           exit( 1 );
00602         }
00603       }
00604       else if ( strcmp( pt, "-ext" ) == 0 )
00605         exts[ecnt++] = argv[++i];
00606       else if ( strcmp( pt, "-help" ) == 0 )
00607       {
00608         usage( stdout, argv[0] );
00609         exit( 0 );
00610       }
00611       else if ( strcmp( pt, "-nohints" ) == 0 )
00612         nohints = true;
00613       else if ( strcmp( pt, "-rasterize" ) == 0 )
00614         rasterize = true;
00615       else if ( strcmp( pt, "-results" ) == 0 )
00616         results_dir = argv[++i];
00617       else if ( strcmp( pt, "-size" ) == 0 )
00618       {
00619         font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 );
00620         if ( *end != '\0' || font_size < 64 )
00621         {
00622           fprintf( stderr, "Bad value for size: %s\n", argv[i] );
00623           exit( 1 );
00624         }
00625       }
00626       else if ( strcmp( pt, "-test" ) == 0 )
00627         testfile = argv[++i];
00628       else
00629       {
00630         usage( stderr, argv[0] );
00631         exit( 1 );
00632       }
00633     }
00634 
00635     if ( allexts )
00636       exts = NULL;
00637     else if ( ecnt == 0 )
00638       exts = default_ext_list;
00639 
00640     if ( dcnt == 0 )
00641       dirs = default_dir_list;
00642 
00643     if ( testfile != NULL )
00644       ExecuteTest( testfile );         /* This should never return */
00645 
00646     time( &now );
00647     srandom( now );
00648 
00649     FindFonts( dirs, exts );
00650     mkdir( results_dir, 0755 );
00651 
00652     forever
00653       do_test();
00654 
00655     return 0;
00656   }
00657 
00658 
00659 /* EOF */

Generated on Tue Jul 5 14:13:53 2011 for ROOT_528-00b_version by  doxygen 1.5.1