Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  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
  103.   FT_MoveTo( const FT_Vector  *to,
  104.              void             *user )
  105.   {
  106.     return 0;
  107.   }
  108.  
  109.  
  110.   static int
  111.   FT_LineTo( const FT_Vector  *to,
  112.              void             *user )
  113.   {
  114.     return 0;
  115.   }
  116.  
  117.  
  118.   static int
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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.