[FREETYPE]
[reactos.git] / reactos / lib / 3rdparty / freetype / src / tools / ftrandom / ftrandom.c
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 */