Home | History | Annotate | Line # | Download | only in ftrandom
      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 (at) 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 <freetype/freetype.h>
     49 #include <freetype/ftoutln.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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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