/* Copyright (C) 2005, 2007, 2008 by George Williams */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* modified by Werner Lemberg */ /* This file is now part of the FreeType library */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include FT_OUTLINE_H #define true 1 #define false 0 #define forever for (;;) static int check_outlines = false; static int nohints = false; static int rasterize = false; static char* results_dir = "results"; #define GOOD_FONTS_DIR "/home/wl/freetype-testfonts" static char* default_dir_list[] = { GOOD_FONTS_DIR, NULL }; static char* default_ext_list[] = { "ttf", "otf", "ttc", "cid", "pfb", "pfa", "bdf", "pcf", "pfr", "fon", "otb", "cff", NULL }; static int error_count = 1; static int error_fraction = 0; static FT_F26Dot6 font_size = 12 * 64; static struct fontlist { char* name; int len; unsigned int isbinary: 1; unsigned int isascii: 1; unsigned int ishex: 1; } *fontlist; static int fcnt; static int FT_MoveTo( const FT_Vector *to, void *user ) { return 0; } static int FT_LineTo( const FT_Vector *to, void *user ) { return 0; } static int FT_ConicTo( const FT_Vector *_cp, const FT_Vector *to, void *user ) { return 0; } static int FT_CubicTo( const FT_Vector *cp1, const FT_Vector *cp2, const FT_Vector *to, void *user ) { return 0; } static FT_Outline_Funcs outlinefuncs = { FT_MoveTo, FT_LineTo, FT_ConicTo, FT_CubicTo, 0, 0 /* No shift, no delta */ }; static void TestFace( FT_Face face ) { int gid; int load_flags = FT_LOAD_DEFAULT; if ( check_outlines && FT_IS_SCALABLE( face ) ) load_flags = FT_LOAD_NO_BITMAP; if ( nohints ) load_flags |= FT_LOAD_NO_HINTING; FT_Set_Char_Size( face, 0, font_size, 72, 72 ); for ( gid = 0; gid < face->num_glyphs; ++gid ) { if ( check_outlines && FT_IS_SCALABLE( face ) ) { if ( !FT_Load_Glyph( face, gid, load_flags ) ) FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL ); } else FT_Load_Glyph( face, gid, load_flags ); if ( rasterize ) FT_Render_Glyph( face->glyph, ft_render_mode_normal ); } FT_Done_Face( face ); } static void ExecuteTest( char* testfont ) { FT_Library context; FT_Face face; int i, num; if ( FT_Init_FreeType( &context ) ) { fprintf( stderr, "Can't initialize FreeType.\n" ); exit( 1 ); } if ( FT_New_Face( context, testfont, 0, &face ) ) { /* The font is erroneous, so if this fails that's ok. */ exit( 0 ); } if ( face->num_faces == 1 ) TestFace( face ); else { num = face->num_faces; FT_Done_Face( face ); for ( i = 0; i < num; ++i ) { if ( !FT_New_Face( context, testfont, i, &face ) ) TestFace( face ); } } exit( 0 ); } static int extmatch( char* filename, char** extensions ) { int i; char* pt; if ( extensions == NULL ) return true; pt = strrchr( filename, '.' ); if ( pt == NULL ) return false; if ( pt < strrchr( filename, '/' ) ) return false; for ( i = 0; extensions[i] != NULL; ++i ) if ( strcasecmp( pt + 1, extensions[i] ) == 0 || strcasecmp( pt, extensions[i] ) == 0 ) return true; return false; } static void figurefiletype( struct fontlist* item ) { FILE* foo; item->isbinary = item->isascii = item->ishex = false; foo = fopen( item->name, "rb" ); if ( foo != NULL ) { /* Try to guess the file type from the first few characters... */ int ch1 = getc( foo ); int ch2 = getc( foo ); int ch3 = getc( foo ); int ch4 = getc( foo ); fclose( foo ); if ( ( ch1 == 0 && ch2 == 1 && ch3 == 0 && ch4 == 0 ) || ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) || ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) || ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) ) { /* ttf, otf, ttc files */ item->isbinary = true; } else if ( ch1 == 0x80 && ch2 == '\01' ) { /* PFB header */ item->isbinary = true; } else if ( ch1 == '%' && ch2 == '!' ) { /* Random PostScript */ if ( strstr( item->name, ".pfa" ) != NULL || strstr( item->name, ".PFA" ) != NULL ) item->ishex = true; else item->isascii = true; } else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 ) { /* Bare CFF */ item->isbinary = true; } else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' ) { /* BDF */ item->ishex = true; } else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' ) { /* PFR */ item->isbinary = true; } else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) || ( ch1 == 'M' && ch2 == 'Z' ) ) { /* Windows FON */ item->isbinary = true; } else { fprintf( stderr, "Can't recognize file type of `%s', assuming binary\n", item->name ); item->isbinary = true; } } else { fprintf( stderr, "Can't open `%s' for typing the file.\n", item->name ); item->isbinary = true; } } static void FindFonts( char** fontdirs, char** extensions ) { DIR* examples; struct dirent* ent; int i, max; char buffer[1025]; struct stat statb; max = 0; fcnt = 0; for ( i = 0; fontdirs[i] != NULL; ++i ) { examples = opendir( fontdirs[i] ); if ( examples == NULL ) { fprintf( stderr, "Can't open example font directory `%s'\n", fontdirs[i] ); exit( 1 ); } while ( ( ent = readdir( examples ) ) != NULL ) { snprintf( buffer, sizeof ( buffer ), "%s/%s", fontdirs[i], ent->d_name ); if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) ) continue; if ( extensions == NULL || extmatch( buffer, extensions ) ) { if ( fcnt >= max ) { max += 100; fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) ); if ( fontlist == NULL ) { fprintf( stderr, "Can't allocate memory\n" ); exit( 1 ); } } fontlist[fcnt].name = strdup( buffer ); fontlist[fcnt].len = statb.st_size; figurefiletype( &fontlist[fcnt] ); ++fcnt; } } closedir( examples ); } if ( fcnt == 0 ) { fprintf( stderr, "Can't find matching font files.\n" ); exit( 1 ); } fontlist[fcnt].name = NULL; } static int getErrorCnt( struct fontlist* item ) { if ( error_count == 0 && error_fraction == 0 ) return 0; return error_count + ceil( error_fraction * item->len ); } static int getRandom( int low, int high ) { if ( low - high < 0x10000L ) return low + ( ( random() >> 8 ) % ( high + 1 - low ) ); return low + ( random() % ( high + 1 - low ) ); } static int copyfont( struct fontlist* item, char* newfont ) { static char buffer[8096]; FILE *good, *new; int len; int i, err_cnt; good = fopen( item->name, "r" ); if ( good == NULL ) { fprintf( stderr, "Can't open `%s'\n", item->name ); return false; } new = fopen( newfont, "w+" ); if ( new == NULL ) { fprintf( stderr, "Can't create temporary output file `%s'\n", newfont ); exit( 1 ); } while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 ) fwrite( buffer, 1, len, new ); fclose( good ); err_cnt = getErrorCnt( item ); for ( i = 0; i < err_cnt; ++i ) { fseek( new, getRandom( 0, item->len - 1 ), SEEK_SET ); if ( item->isbinary ) putc( getRandom( 0, 0xff ), new ); else if ( item->isascii ) putc( getRandom( 0x20, 0x7e ), new ); else { int hex = getRandom( 0, 15 ); if ( hex < 10 ) hex += '0'; else hex += 'A' - 10; putc( hex, new ); } } if ( ferror( new ) ) { fclose( new ); unlink( newfont ); return false; } fclose( new ); return true; } static int child_pid; static void abort_test( int sig ) { /* If a time-out happens, then kill the child */ kill( child_pid, SIGFPE ); write( 2, "Timeout... ", 11 ); } static void do_test( void ) { int i = getRandom( 0, fcnt - 1 ); static int test_num = 0; char buffer[1024]; sprintf( buffer, "%s/test%d", results_dir, test_num++ ); if ( copyfont ( &fontlist[i], buffer ) ) { signal( SIGALRM, abort_test ); /* Anything that takes more than 20 seconds */ /* to parse and/or rasterize is an error. */ alarm( 20 ); if ( ( child_pid = fork() ) == 0 ) ExecuteTest( buffer ); else if ( child_pid != -1 ) { int status; waitpid( child_pid, &status, 0 ); alarm( 0 ); if ( WIFSIGNALED ( status ) ) printf( "Error found in file `%s'\n", buffer ); else unlink( buffer ); } else { fprintf( stderr, "Can't fork test case.\n" ); exit( 1 ); } alarm( 0 ); } } static void usage( FILE* out, char* name ) { fprintf( out, "%s [options] -- Generate random erroneous fonts\n" " and attempt to parse them with FreeType.\n\n", name ); fprintf( out, " --all All non-directory files are assumed to be fonts.\n" ); fprintf( out, " --check-outlines Make sure we can parse the outlines of each glyph.\n" ); fprintf( out, " --dir Append to list of font search directories.\n" ); fprintf( out, " --error-count Introduce single byte errors into each font.\n" ); fprintf( out, " --error-fraction Introduce *filesize single byte errors\n" " into each font.\n" ); fprintf( out, " --ext Add to list of extensions indicating fonts.\n" ); fprintf( out, " --help Print this.\n" ); fprintf( out, " --nohints Turn off hinting.\n" ); fprintf( out, " --rasterize Attempt to rasterize each glyph.\n" ); fprintf( out, " --results Directory in which to place the test fonts.\n" ); fprintf( out, " --size Use the given font size for the tests.\n" ); fprintf( out, " --test Run a single test on an already existing file.\n" ); } int main( int argc, char** argv ) { char **dirs, **exts; char *pt, *end; int dcnt = 0, ecnt = 0, rset = false, allexts = false; int i; time_t now; char* testfile = NULL; dirs = calloc( argc + 1, sizeof ( char ** ) ); exts = calloc( argc + 1, sizeof ( char ** ) ); for ( i = 1; i < argc; ++i ) { pt = argv[i]; if ( pt[0] == '-' && pt[1] == '-' ) ++pt; if ( strcmp( pt, "-all" ) == 0 ) allexts = true; else if ( strcmp( pt, "-check-outlines" ) == 0 ) check_outlines = true; else if ( strcmp( pt, "-dir" ) == 0 ) dirs[dcnt++] = argv[++i]; else if ( strcmp( pt, "-error-count" ) == 0 ) { if ( !rset ) error_fraction = 0; rset = true; error_count = strtol( argv[++i], &end, 10 ); if ( *end != '\0' ) { fprintf( stderr, "Bad value for error-count: %s\n", argv[i] ); exit( 1 ); } } else if ( strcmp( pt, "-error-fraction" ) == 0 ) { if ( !rset ) error_count = 0; rset = true; error_fraction = strtod( argv[++i], &end ); if ( *end != '\0' ) { fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] ); exit( 1 ); } } else if ( strcmp( pt, "-ext" ) == 0 ) exts[ecnt++] = argv[++i]; else if ( strcmp( pt, "-help" ) == 0 ) { usage( stdout, argv[0] ); exit( 0 ); } else if ( strcmp( pt, "-nohints" ) == 0 ) nohints = true; else if ( strcmp( pt, "-rasterize" ) == 0 ) rasterize = true; else if ( strcmp( pt, "-results" ) == 0 ) results_dir = argv[++i]; else if ( strcmp( pt, "-size" ) == 0 ) { font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 ); if ( *end != '\0' || font_size < 64 ) { fprintf( stderr, "Bad value for size: %s\n", argv[i] ); exit( 1 ); } } else if ( strcmp( pt, "-test" ) == 0 ) testfile = argv[++i]; else { usage( stderr, argv[0] ); exit( 1 ); } } if ( allexts ) exts = NULL; else if ( ecnt == 0 ) exts = default_ext_list; if ( dcnt == 0 ) dirs = default_dir_list; if ( testfile != NULL ) ExecuteTest( testfile ); /* This should never return */ time( &now ); srandom( now ); FindFonts( dirs, exts ); mkdir( results_dir, 0755 ); forever do_test(); return 0; } /* EOF */