1 /* Copyright (C) 2005, 2007, 2008, 2013 by George Williams */ 2 /* 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are met: 5 6 * Redistributions of source code must retain the above copyright notice, this 7 * list of conditions and the following disclaimer. 8 9 * Redistributions in binary form must reproduce the above copyright notice, 10 * this list of conditions and the following disclaimer in the documentation 11 * and/or other materials provided with the distribution. 12 13 * The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* modified by Werner Lemberg <wl@gnu.org> */ 29 /* This file is now part of the FreeType library */ 30 31 32 #define _XOPEN_SOURCE 500 /* for `kill', `strdup', `random', and `srandom' */ 33 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <strings.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/wait.h> 42 #include <unistd.h> 43 #include <dirent.h> 44 #include <signal.h> 45 #include <time.h> 46 47 #include <ft2build.h> 48 #include FT_FREETYPE_H 49 #include FT_OUTLINE_H 50 51 #define true 1 52 #define false 0 53 #define forever for (;;) 54 55 56 static int check_outlines = false; 57 static int nohints = false; 58 static int rasterize = false; 59 static char* results_dir = "results"; 60 61 #define GOOD_FONTS_DIR "/usr/local/share/fonts" 62 63 static char* default_dir_list[] = 64 { 65 GOOD_FONTS_DIR, 66 NULL 67 }; 68 69 static char* default_ext_list[] = 70 { 71 "ttf", 72 "otf", 73 "ttc", 74 "cid", 75 "pfb", 76 "pfa", 77 "bdf", 78 "pcf", 79 "pfr", 80 "fon", 81 "otb", 82 "cff", 83 NULL 84 }; 85 86 static unsigned int error_count = 1; 87 static double error_fraction = 0.0; 88 89 static FT_F26Dot6 font_size = 12 * 64; 90 91 static struct fontlist 92 { 93 char* name; 94 long len; 95 unsigned int isbinary: 1; 96 unsigned int isascii: 1; 97 unsigned int ishex: 1; 98 99 } *fontlist; 100 101 static unsigned int fcnt; 102 103 104 static int FT_MoveTo(const FT_Vector * to,void * user)105 FT_MoveTo( const FT_Vector *to, 106 void *user ) 107 { 108 FT_UNUSED( to ); 109 FT_UNUSED( user ); 110 111 return 0; 112 } 113 114 115 static int FT_LineTo(const FT_Vector * to,void * user)116 FT_LineTo( const FT_Vector *to, 117 void *user ) 118 { 119 FT_UNUSED( to ); 120 FT_UNUSED( user ); 121 122 return 0; 123 } 124 125 126 static int FT_ConicTo(const FT_Vector * _cp,const FT_Vector * to,void * user)127 FT_ConicTo( const FT_Vector *_cp, 128 const FT_Vector *to, 129 void *user ) 130 { 131 FT_UNUSED( _cp ); 132 FT_UNUSED( to ); 133 FT_UNUSED( user ); 134 135 return 0; 136 } 137 138 139 static int FT_CubicTo(const FT_Vector * cp1,const FT_Vector * cp2,const FT_Vector * to,void * user)140 FT_CubicTo( const FT_Vector *cp1, 141 const FT_Vector *cp2, 142 const FT_Vector *to, 143 void *user ) 144 { 145 FT_UNUSED( cp1 ); 146 FT_UNUSED( cp2 ); 147 FT_UNUSED( to ); 148 FT_UNUSED( user ); 149 150 return 0; 151 } 152 153 154 static FT_Outline_Funcs outlinefuncs = 155 { 156 FT_MoveTo, 157 FT_LineTo, 158 FT_ConicTo, 159 FT_CubicTo, 160 0, 0 /* No shift, no delta */ 161 }; 162 163 164 static void TestFace(FT_Face face)165 TestFace( FT_Face face ) 166 { 167 unsigned int gid; 168 int load_flags = FT_LOAD_DEFAULT; 169 170 171 if ( check_outlines && 172 FT_IS_SCALABLE( face ) ) 173 load_flags = FT_LOAD_NO_BITMAP; 174 175 if ( nohints ) 176 load_flags |= FT_LOAD_NO_HINTING; 177 178 FT_Set_Char_Size( face, 0, font_size, 72, 72 ); 179 180 for ( gid = 0; gid < face->num_glyphs; gid++ ) 181 { 182 if ( check_outlines && 183 FT_IS_SCALABLE( face ) ) 184 { 185 if ( !FT_Load_Glyph( face, gid, load_flags ) ) 186 FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL ); 187 } 188 else 189 FT_Load_Glyph( face, gid, load_flags ); 190 191 if ( rasterize ) 192 FT_Render_Glyph( face->glyph, ft_render_mode_normal ); 193 } 194 195 FT_Done_Face( face ); 196 } 197 198 199 static void ExecuteTest(char * testfont)200 ExecuteTest( char* testfont ) 201 { 202 FT_Library context; 203 FT_Face face; 204 205 206 if ( FT_Init_FreeType( &context ) ) 207 { 208 fprintf( stderr, "Can't initialize FreeType.\n" ); 209 exit( 1 ); 210 } 211 212 if ( FT_New_Face( context, testfont, 0, &face ) ) 213 { 214 /* The font is erroneous, so if this fails that's ok. */ 215 exit( 0 ); 216 } 217 218 if ( face->num_faces == 1 ) 219 TestFace( face ); 220 else 221 { 222 long i, num; 223 224 225 num = face->num_faces; 226 FT_Done_Face( face ); 227 228 for ( i = 0; i < num; i++ ) 229 { 230 if ( !FT_New_Face( context, testfont, i, &face ) ) 231 TestFace( face ); 232 } 233 } 234 235 FT_Done_FreeType( context ); 236 237 exit( 0 ); 238 } 239 240 241 static int extmatch(char * filename,char ** extensions)242 extmatch( char* filename, 243 char** extensions ) 244 { 245 int i; 246 char* pt; 247 248 249 if ( !extensions ) 250 return true; 251 252 pt = strrchr( filename, '.' ); 253 if ( !pt ) 254 return false; 255 if ( pt < strrchr( filename, '/' ) ) 256 return false; 257 258 for ( i = 0; extensions[i] != NULL; i++ ) 259 if ( strcasecmp( pt + 1, extensions[i] ) == 0 || 260 strcasecmp( pt, extensions[i] ) == 0 ) 261 return true; 262 263 return false; 264 } 265 266 267 static void figurefiletype(struct fontlist * item)268 figurefiletype( struct fontlist* item ) 269 { 270 FILE* foo; 271 272 273 item->isbinary = item->isascii = item->ishex = false; 274 275 foo = fopen( item->name, "rb" ); 276 if ( foo ) 277 { 278 /* Try to guess the file type from the first few characters... */ 279 int ch1 = getc( foo ); 280 int ch2 = getc( foo ); 281 int ch3 = getc( foo ); 282 int ch4 = getc( foo ); 283 284 285 fclose( foo ); 286 287 if ( ( ch1 == 0 && ch2 == 1 && ch3 == 0 && ch4 == 0 ) || 288 ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) || 289 ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) || 290 ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) ) 291 { 292 /* ttf, otf, ttc files */ 293 item->isbinary = true; 294 } 295 else if ( ch1 == 0x80 && ch2 == '\01' ) 296 { 297 /* PFB header */ 298 item->isbinary = true; 299 } 300 else if ( ch1 == '%' && ch2 == '!' ) 301 { 302 /* Random PostScript */ 303 if ( strstr( item->name, ".pfa" ) || 304 strstr( item->name, ".PFA" ) ) 305 item->ishex = true; 306 else 307 item->isascii = true; 308 } 309 else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 ) 310 { 311 /* Bare CFF */ 312 item->isbinary = true; 313 } 314 else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' ) 315 { 316 /* BDF */ 317 item->ishex = true; 318 } 319 else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' ) 320 { 321 /* PFR */ 322 item->isbinary = true; 323 } 324 else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) || 325 ( ch1 == 'M' && ch2 == 'Z' ) ) 326 { 327 /* Windows FON */ 328 item->isbinary = true; 329 } 330 else 331 { 332 fprintf( stderr, 333 "Can't recognize file type of `%s', assuming binary\n", 334 item->name ); 335 item->isbinary = true; 336 } 337 } 338 else 339 { 340 fprintf( stderr, "Can't open `%s' for typing the file.\n", 341 item->name ); 342 item->isbinary = true; 343 } 344 } 345 346 347 static void FindFonts(char ** fontdirs,char ** extensions)348 FindFonts( char** fontdirs, 349 char** extensions ) 350 { 351 int i; 352 unsigned int max; 353 char buffer[1025]; 354 struct stat statb; 355 356 357 max = 0; 358 fcnt = 0; 359 360 for ( i = 0; fontdirs[i] != NULL; i++ ) 361 { 362 DIR* examples; 363 struct dirent* ent; 364 365 366 examples = opendir( fontdirs[i] ); 367 if ( !examples ) 368 { 369 fprintf( stderr, 370 "Can't open example font directory `%s'\n", 371 fontdirs[i] ); 372 exit( 1 ); 373 } 374 375 while ( ( ent = readdir( examples ) ) != NULL ) 376 { 377 snprintf( buffer, sizeof ( buffer ), 378 "%s/%s", fontdirs[i], ent->d_name ); 379 if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) ) 380 continue; 381 if ( !extensions || extmatch( buffer, extensions ) ) 382 { 383 if ( fcnt >= max ) 384 { 385 max += 100; 386 fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) ); 387 if ( !fontlist ) 388 { 389 fprintf( stderr, "Can't allocate memory\n" ); 390 exit( 1 ); 391 } 392 } 393 394 fontlist[fcnt].name = strdup( buffer ); 395 fontlist[fcnt].len = statb.st_size; 396 397 figurefiletype( &fontlist[fcnt] ); 398 fcnt++; 399 } 400 } 401 402 closedir( examples ); 403 } 404 405 if ( fcnt == 0 ) 406 { 407 fprintf( stderr, "Can't find matching font files.\n" ); 408 exit( 1 ); 409 } 410 411 fontlist[fcnt].name = NULL; 412 } 413 414 415 static unsigned int getErrorCnt(struct fontlist * item)416 getErrorCnt( struct fontlist* item ) 417 { 418 if ( error_count == 0 && error_fraction == 0.0 ) 419 return 0; 420 421 return error_count + (unsigned int)( error_fraction * item->len ); 422 } 423 424 425 static int getRandom(int low,int high)426 getRandom( int low, 427 int high ) 428 { 429 if ( low - high < 0x10000L ) 430 return low + ( ( random() >> 8 ) % ( high + 1 - low ) ); 431 432 return low + ( random() % ( high + 1 - low ) ); 433 } 434 435 436 static int copyfont(struct fontlist * item,char * newfont)437 copyfont( struct fontlist* item, 438 char* newfont ) 439 { 440 static char buffer[8096]; 441 FILE *good, *newf; 442 size_t len; 443 unsigned int i, err_cnt; 444 445 446 good = fopen( item->name, "r" ); 447 if ( !good ) 448 { 449 fprintf( stderr, "Can't open `%s'\n", item->name ); 450 return false; 451 } 452 453 newf = fopen( newfont, "w+" ); 454 if ( !newf ) 455 { 456 fprintf( stderr, "Can't create temporary output file `%s'\n", 457 newfont ); 458 exit( 1 ); 459 } 460 461 while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 ) 462 fwrite( buffer, 1, len, newf ); 463 464 fclose( good ); 465 466 err_cnt = getErrorCnt( item ); 467 for ( i = 0; i < err_cnt; i++ ) 468 { 469 fseek( newf, getRandom( 0, (int)( item->len - 1 ) ), SEEK_SET ); 470 471 if ( item->isbinary ) 472 putc( getRandom( 0, 0xFF ), newf ); 473 else if ( item->isascii ) 474 putc( getRandom( 0x20, 0x7E ), newf ); 475 else 476 { 477 int hex = getRandom( 0, 15 ); 478 479 480 if ( hex < 10 ) 481 hex += '0'; 482 else 483 hex += 'A' - 10; 484 485 putc( hex, newf ); 486 } 487 } 488 489 if ( ferror( newf ) ) 490 { 491 fclose( newf ); 492 unlink( newfont ); 493 return false; 494 } 495 496 fclose( newf ); 497 498 return true; 499 } 500 501 502 static int child_pid; 503 504 static void abort_test(int sig)505 abort_test( int sig ) 506 { 507 FT_UNUSED( sig ); 508 509 /* If a time-out happens, then kill the child */ 510 kill( child_pid, SIGFPE ); 511 write( 2, "Timeout... ", 11 ); 512 } 513 514 515 static void do_test(void)516 do_test( void ) 517 { 518 int i = getRandom( 0, (int)( fcnt - 1 ) ); 519 static int test_num = 0; 520 char buffer[1024]; 521 522 523 sprintf( buffer, "%s/test%d", results_dir, test_num++ ); 524 525 if ( copyfont ( &fontlist[i], buffer ) ) 526 { 527 signal( SIGALRM, abort_test ); 528 /* Anything that takes more than 20 seconds */ 529 /* to parse and/or rasterize is an error. */ 530 alarm( 20 ); 531 if ( ( child_pid = fork() ) == 0 ) 532 ExecuteTest( buffer ); 533 else if ( child_pid != -1 ) 534 { 535 int status; 536 537 538 waitpid( child_pid, &status, 0 ); 539 alarm( 0 ); 540 if ( WIFSIGNALED ( status ) ) 541 printf( "Error found in file `%s'\n", buffer ); 542 else 543 unlink( buffer ); 544 } 545 else 546 { 547 fprintf( stderr, "Can't fork test case.\n" ); 548 exit( 1 ); 549 } 550 alarm( 0 ); 551 } 552 } 553 554 555 static void usage(FILE * out,char * name)556 usage( FILE* out, 557 char* name ) 558 { 559 char** d = default_dir_list; 560 char** e = default_ext_list; 561 562 563 fprintf( out, "%s [options] -- Generate random erroneous fonts\n" 564 " and attempt to parse them with FreeType.\n\n", name ); 565 566 fprintf( out, " --all All non-directory files are assumed to be fonts.\n" ); 567 fprintf( out, " --check-outlines Make sure we can parse the outlines of each glyph.\n" ); 568 fprintf( out, " --dir <path> Append <path> to list of font search directories\n" 569 " (no recursive search).\n" ); 570 fprintf( out, " --error-count <cnt> Introduce <cnt> single byte errors into each font\n" 571 " (default: 1)\n" ); 572 fprintf( out, " --error-fraction <frac> Introduce <frac>*filesize single byte errors\n" 573 " into each font (default: 0.0).\n" ); 574 fprintf( out, " --ext <ext> Add <ext> to list of extensions indicating fonts.\n" ); 575 fprintf( out, " --help Print this.\n" ); 576 fprintf( out, " --nohints Turn off hinting.\n" ); 577 fprintf( out, " --rasterize Attempt to rasterize each glyph.\n" ); 578 fprintf( out, " --results <path> Place the created test fonts into <path>\n" 579 " (default: `results')\n" ); 580 fprintf( out, " --size <float> Use the given font size for the tests.\n" ); 581 fprintf( out, " --test <file> Run a single test on an already existing file.\n" ); 582 fprintf( out, "\n" ); 583 584 fprintf( out, "Default font extensions:\n" ); 585 fprintf( out, " " ); 586 while ( *e ) 587 fprintf( out, " .%s", *e++ ); 588 fprintf( out, "\n" ); 589 590 fprintf( out, "Default font directories:\n" ); 591 fprintf( out, " " ); 592 while ( *d ) 593 fprintf( out, " %s", *d++ ); 594 fprintf( out, "\n" ); 595 } 596 597 598 int main(int argc,char ** argv)599 main( int argc, 600 char** argv ) 601 { 602 char **dirs, **exts; 603 int dcnt = 0, ecnt = 0, rset = false, allexts = false; 604 int i; 605 time_t now; 606 char* testfile = NULL; 607 608 609 dirs = calloc( (size_t)( argc + 1 ), sizeof ( char ** ) ); 610 exts = calloc( (size_t)( argc + 1 ), sizeof ( char ** ) ); 611 612 for ( i = 1; i < argc; i++ ) 613 { 614 char* pt = argv[i]; 615 char* end; 616 617 618 if ( pt[0] == '-' && pt[1] == '-' ) 619 pt++; 620 621 if ( strcmp( pt, "-all" ) == 0 ) 622 allexts = true; 623 else if ( strcmp( pt, "-check-outlines" ) == 0 ) 624 check_outlines = true; 625 else if ( strcmp( pt, "-dir" ) == 0 ) 626 dirs[dcnt++] = argv[++i]; 627 else if ( strcmp( pt, "-error-count" ) == 0 ) 628 { 629 if ( !rset ) 630 error_fraction = 0.0; 631 rset = true; 632 error_count = (unsigned int)strtoul( argv[++i], &end, 10 ); 633 if ( *end != '\0' ) 634 { 635 fprintf( stderr, "Bad value for error-count: %s\n", argv[i] ); 636 exit( 1 ); 637 } 638 } 639 else if ( strcmp( pt, "-error-fraction" ) == 0 ) 640 { 641 if ( !rset ) 642 error_count = 0; 643 rset = true; 644 error_fraction = strtod( argv[++i], &end ); 645 if ( *end != '\0' ) 646 { 647 fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] ); 648 exit( 1 ); 649 } 650 if ( error_fraction < 0.0 || error_fraction > 1.0 ) 651 { 652 fprintf( stderr, "error-fraction must be in the range [0;1]\n" ); 653 exit( 1 ); 654 } 655 } 656 else if ( strcmp( pt, "-ext" ) == 0 ) 657 exts[ecnt++] = argv[++i]; 658 else if ( strcmp( pt, "-help" ) == 0 ) 659 { 660 usage( stdout, argv[0] ); 661 exit( 0 ); 662 } 663 else if ( strcmp( pt, "-nohints" ) == 0 ) 664 nohints = true; 665 else if ( strcmp( pt, "-rasterize" ) == 0 ) 666 rasterize = true; 667 else if ( strcmp( pt, "-results" ) == 0 ) 668 results_dir = argv[++i]; 669 else if ( strcmp( pt, "-size" ) == 0 ) 670 { 671 font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 ); 672 if ( *end != '\0' || font_size < 64 ) 673 { 674 fprintf( stderr, "Bad value for size: %s\n", argv[i] ); 675 exit( 1 ); 676 } 677 } 678 else if ( strcmp( pt, "-test" ) == 0 ) 679 testfile = argv[++i]; 680 else 681 { 682 usage( stderr, argv[0] ); 683 exit( 1 ); 684 } 685 } 686 687 if ( allexts ) 688 { 689 free( exts ); 690 exts = NULL; 691 } 692 else if ( ecnt == 0 ) 693 { 694 free( exts ); 695 exts = default_ext_list; 696 } 697 698 if ( dcnt == 0 ) 699 { 700 free( dirs ); 701 dirs = default_dir_list; 702 } 703 704 if ( testfile ) 705 ExecuteTest( testfile ); /* This should never return */ 706 707 time( &now ); 708 srandom( (unsigned int)now ); 709 710 FindFonts( dirs, exts ); 711 mkdir( results_dir, 0755 ); 712 713 forever 714 do_test(); 715 716 return 0; 717 } 718 719 720 /* EOF */ 721