00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
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
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
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
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
00272 item->isbinary = true;
00273 }
00274 else if ( ch1 == 0x80 && ch2 == '\01' )
00275 {
00276
00277 item->isbinary = true;
00278 }
00279 else if ( ch1 == '%' && ch2 == '!' )
00280 {
00281
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
00291 item->isbinary = true;
00292 }
00293 else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' )
00294 {
00295
00296 item->ishex = true;
00297 }
00298 else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' )
00299 {
00300
00301 item->isbinary = true;
00302 }
00303 else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) ||
00304 ( ch1 == 'M' && ch2 == 'Z' ) )
00305 {
00306
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
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
00504
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 );
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