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