|
1 /* Copyright (C) 2005, 2007, 2008 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 int i, num; |
|
188 |
|
189 |
|
190 if ( FT_Init_FreeType( &context ) ) |
|
191 { |
|
192 fprintf( stderr, "Can't initialize FreeType.\n" ); |
|
193 exit( 1 ); |
|
194 } |
|
195 |
|
196 if ( FT_New_Face( context, testfont, 0, &face ) ) |
|
197 { |
|
198 /* The font is erroneous, so if this fails that's ok. */ |
|
199 exit( 0 ); |
|
200 } |
|
201 |
|
202 if ( face->num_faces == 1 ) |
|
203 TestFace( face ); |
|
204 else |
|
205 { |
|
206 num = face->num_faces; |
|
207 FT_Done_Face( face ); |
|
208 |
|
209 for ( i = 0; i < num; ++i ) |
|
210 { |
|
211 if ( !FT_New_Face( context, testfont, i, &face ) ) |
|
212 TestFace( face ); |
|
213 } |
|
214 } |
|
215 |
|
216 exit( 0 ); |
|
217 } |
|
218 |
|
219 |
|
220 static int |
|
221 extmatch( char* filename, |
|
222 char** extensions ) |
|
223 { |
|
224 int i; |
|
225 char* pt; |
|
226 |
|
227 |
|
228 if ( extensions == NULL ) |
|
229 return true; |
|
230 |
|
231 pt = strrchr( filename, '.' ); |
|
232 if ( pt == NULL ) |
|
233 return false; |
|
234 if ( pt < strrchr( filename, '/' ) ) |
|
235 return false; |
|
236 |
|
237 for ( i = 0; extensions[i] != NULL; ++i ) |
|
238 if ( strcasecmp( pt + 1, extensions[i] ) == 0 || |
|
239 strcasecmp( pt, extensions[i] ) == 0 ) |
|
240 return true; |
|
241 |
|
242 return false; |
|
243 } |
|
244 |
|
245 |
|
246 static void |
|
247 figurefiletype( struct fontlist* item ) |
|
248 { |
|
249 FILE* foo; |
|
250 |
|
251 |
|
252 item->isbinary = item->isascii = item->ishex = false; |
|
253 |
|
254 foo = fopen( item->name, "rb" ); |
|
255 if ( foo != NULL ) |
|
256 { |
|
257 /* Try to guess the file type from the first few characters... */ |
|
258 int ch1 = getc( foo ); |
|
259 int ch2 = getc( foo ); |
|
260 int ch3 = getc( foo ); |
|
261 int ch4 = getc( foo ); |
|
262 |
|
263 |
|
264 fclose( foo ); |
|
265 |
|
266 if ( ( ch1 == 0 && ch2 == 1 && ch3 == 0 && ch4 == 0 ) || |
|
267 ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) || |
|
268 ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) || |
|
269 ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) ) |
|
270 { |
|
271 /* ttf, otf, ttc files */ |
|
272 item->isbinary = true; |
|
273 } |
|
274 else if ( ch1 == 0x80 && ch2 == '\01' ) |
|
275 { |
|
276 /* PFB header */ |
|
277 item->isbinary = true; |
|
278 } |
|
279 else if ( ch1 == '%' && ch2 == '!' ) |
|
280 { |
|
281 /* Random PostScript */ |
|
282 if ( strstr( item->name, ".pfa" ) != NULL || |
|
283 strstr( item->name, ".PFA" ) != NULL ) |
|
284 item->ishex = true; |
|
285 else |
|
286 item->isascii = true; |
|
287 } |
|
288 else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 ) |
|
289 { |
|
290 /* Bare CFF */ |
|
291 item->isbinary = true; |
|
292 } |
|
293 else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' ) |
|
294 { |
|
295 /* BDF */ |
|
296 item->ishex = true; |
|
297 } |
|
298 else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' ) |
|
299 { |
|
300 /* PFR */ |
|
301 item->isbinary = true; |
|
302 } |
|
303 else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) || |
|
304 ( ch1 == 'M' && ch2 == 'Z' ) ) |
|
305 { |
|
306 /* Windows FON */ |
|
307 item->isbinary = true; |
|
308 } |
|
309 else |
|
310 { |
|
311 fprintf( stderr, |
|
312 "Can't recognize file type of `%s', assuming binary\n", |
|
313 item->name ); |
|
314 item->isbinary = true; |
|
315 } |
|
316 } |
|
317 else |
|
318 { |
|
319 fprintf( stderr, "Can't open `%s' for typing the file.\n", |
|
320 item->name ); |
|
321 item->isbinary = true; |
|
322 } |
|
323 } |
|
324 |
|
325 |
|
326 static void |
|
327 FindFonts( char** fontdirs, |
|
328 char** extensions ) |
|
329 { |
|
330 DIR* examples; |
|
331 struct dirent* ent; |
|
332 |
|
333 int i, max; |
|
334 char buffer[1025]; |
|
335 struct stat statb; |
|
336 |
|
337 |
|
338 max = 0; |
|
339 fcnt = 0; |
|
340 |
|
341 for ( i = 0; fontdirs[i] != NULL; ++i ) |
|
342 { |
|
343 examples = opendir( fontdirs[i] ); |
|
344 if ( examples == NULL ) |
|
345 { |
|
346 fprintf( stderr, |
|
347 "Can't open example font directory `%s'\n", |
|
348 fontdirs[i] ); |
|
349 exit( 1 ); |
|
350 } |
|
351 |
|
352 while ( ( ent = readdir( examples ) ) != NULL ) |
|
353 { |
|
354 snprintf( buffer, sizeof ( buffer ), |
|
355 "%s/%s", fontdirs[i], ent->d_name ); |
|
356 if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) ) |
|
357 continue; |
|
358 if ( extensions == NULL || extmatch( buffer, extensions ) ) |
|
359 { |
|
360 if ( fcnt >= max ) |
|
361 { |
|
362 max += 100; |
|
363 fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) ); |
|
364 if ( fontlist == NULL ) |
|
365 { |
|
366 fprintf( stderr, "Can't allocate memory\n" ); |
|
367 exit( 1 ); |
|
368 } |
|
369 } |
|
370 |
|
371 fontlist[fcnt].name = strdup( buffer ); |
|
372 fontlist[fcnt].len = statb.st_size; |
|
373 |
|
374 figurefiletype( &fontlist[fcnt] ); |
|
375 ++fcnt; |
|
376 } |
|
377 } |
|
378 |
|
379 closedir( examples ); |
|
380 } |
|
381 |
|
382 if ( fcnt == 0 ) |
|
383 { |
|
384 fprintf( stderr, "Can't find matching font files.\n" ); |
|
385 exit( 1 ); |
|
386 } |
|
387 |
|
388 fontlist[fcnt].name = NULL; |
|
389 } |
|
390 |
|
391 |
|
392 static int |
|
393 getErrorCnt( struct fontlist* item ) |
|
394 { |
|
395 if ( error_count == 0 && error_fraction == 0 ) |
|
396 return 0; |
|
397 |
|
398 return error_count + ceil( error_fraction * item->len ); |
|
399 } |
|
400 |
|
401 |
|
402 static int |
|
403 getRandom( int low, |
|
404 int high ) |
|
405 { |
|
406 if ( low - high < 0x10000L ) |
|
407 return low + ( ( random() >> 8 ) % ( high + 1 - low ) ); |
|
408 |
|
409 return low + ( random() % ( high + 1 - low ) ); |
|
410 } |
|
411 |
|
412 |
|
413 static int |
|
414 copyfont( struct fontlist* item, |
|
415 char* newfont ) |
|
416 { |
|
417 static char buffer[8096]; |
|
418 FILE *good, *new; |
|
419 int len; |
|
420 int i, err_cnt; |
|
421 |
|
422 |
|
423 good = fopen( item->name, "r" ); |
|
424 if ( good == NULL ) |
|
425 { |
|
426 fprintf( stderr, "Can't open `%s'\n", item->name ); |
|
427 return false; |
|
428 } |
|
429 |
|
430 new = fopen( newfont, "w+" ); |
|
431 if ( new == NULL ) |
|
432 { |
|
433 fprintf( stderr, "Can't create temporary output file `%s'\n", |
|
434 newfont ); |
|
435 exit( 1 ); |
|
436 } |
|
437 |
|
438 while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 ) |
|
439 fwrite( buffer, 1, len, new ); |
|
440 |
|
441 fclose( good ); |
|
442 |
|
443 err_cnt = getErrorCnt( item ); |
|
444 for ( i = 0; i < err_cnt; ++i ) |
|
445 { |
|
446 fseek( new, getRandom( 0, item->len - 1 ), SEEK_SET ); |
|
447 |
|
448 if ( item->isbinary ) |
|
449 putc( getRandom( 0, 0xff ), new ); |
|
450 else if ( item->isascii ) |
|
451 putc( getRandom( 0x20, 0x7e ), new ); |
|
452 else |
|
453 { |
|
454 int hex = getRandom( 0, 15 ); |
|
455 |
|
456 |
|
457 if ( hex < 10 ) |
|
458 hex += '0'; |
|
459 else |
|
460 hex += 'A' - 10; |
|
461 |
|
462 putc( hex, new ); |
|
463 } |
|
464 } |
|
465 |
|
466 if ( ferror( new ) ) |
|
467 { |
|
468 fclose( new ); |
|
469 unlink( newfont ); |
|
470 return false; |
|
471 } |
|
472 |
|
473 fclose( new ); |
|
474 |
|
475 return true; |
|
476 } |
|
477 |
|
478 |
|
479 static int child_pid; |
|
480 |
|
481 static void |
|
482 abort_test( int sig ) |
|
483 { |
|
484 /* If a time-out happens, then kill the child */ |
|
485 kill( child_pid, SIGFPE ); |
|
486 write( 2, "Timeout... ", 11 ); |
|
487 } |
|
488 |
|
489 |
|
490 static void |
|
491 do_test( void ) |
|
492 { |
|
493 int i = getRandom( 0, fcnt - 1 ); |
|
494 static int test_num = 0; |
|
495 char buffer[1024]; |
|
496 |
|
497 |
|
498 sprintf( buffer, "%s/test%d", results_dir, test_num++ ); |
|
499 |
|
500 if ( copyfont ( &fontlist[i], buffer ) ) |
|
501 { |
|
502 signal( SIGALRM, abort_test ); |
|
503 /* Anything that takes more than 20 seconds */ |
|
504 /* to parse and/or rasterize is an error. */ |
|
505 alarm( 20 ); |
|
506 if ( ( child_pid = fork() ) == 0 ) |
|
507 ExecuteTest( buffer ); |
|
508 else if ( child_pid != -1 ) |
|
509 { |
|
510 int status; |
|
511 |
|
512 |
|
513 waitpid( child_pid, &status, 0 ); |
|
514 alarm( 0 ); |
|
515 if ( WIFSIGNALED ( status ) ) |
|
516 printf( "Error found in file `%s'\n", buffer ); |
|
517 else |
|
518 unlink( buffer ); |
|
519 } |
|
520 else |
|
521 { |
|
522 fprintf( stderr, "Can't fork test case.\n" ); |
|
523 exit( 1 ); |
|
524 } |
|
525 alarm( 0 ); |
|
526 } |
|
527 } |
|
528 |
|
529 |
|
530 static void |
|
531 usage( FILE* out, |
|
532 char* name ) |
|
533 { |
|
534 fprintf( out, "%s [options] -- Generate random erroneous fonts\n" |
|
535 " and attempt to parse them with FreeType.\n\n", name ); |
|
536 |
|
537 fprintf( out, " --all All non-directory files are assumed to be fonts.\n" ); |
|
538 fprintf( out, " --check-outlines Make sure we can parse the outlines of each glyph.\n" ); |
|
539 fprintf( out, " --dir <path> Append <path> to list of font search directories.\n" ); |
|
540 fprintf( out, " --error-count <cnt> Introduce <cnt> single byte errors into each font.\n" ); |
|
541 fprintf( out, " --error-fraction <frac> Introduce <frac>*filesize single byte errors\n" |
|
542 " into each font.\n" ); |
|
543 fprintf( out, " --ext <ext> Add <ext> to list of extensions indicating fonts.\n" ); |
|
544 fprintf( out, " --help Print this.\n" ); |
|
545 fprintf( out, " --nohints Turn off hinting.\n" ); |
|
546 fprintf( out, " --rasterize Attempt to rasterize each glyph.\n" ); |
|
547 fprintf( out, " --results <dir> Directory in which to place the test fonts.\n" ); |
|
548 fprintf( out, " --size <float> Use the given font size for the tests.\n" ); |
|
549 fprintf( out, " --test <file> Run a single test on an already existing file.\n" ); |
|
550 } |
|
551 |
|
552 |
|
553 int |
|
554 main( int argc, |
|
555 char** argv ) |
|
556 { |
|
557 char **dirs, **exts; |
|
558 char *pt, *end; |
|
559 int dcnt = 0, ecnt = 0, rset = false, allexts = false; |
|
560 int i; |
|
561 time_t now; |
|
562 char* testfile = NULL; |
|
563 |
|
564 |
|
565 dirs = calloc( argc + 1, sizeof ( char ** ) ); |
|
566 exts = calloc( argc + 1, sizeof ( char ** ) ); |
|
567 |
|
568 for ( i = 1; i < argc; ++i ) |
|
569 { |
|
570 pt = argv[i]; |
|
571 if ( pt[0] == '-' && pt[1] == '-' ) |
|
572 ++pt; |
|
573 |
|
574 if ( strcmp( pt, "-all" ) == 0 ) |
|
575 allexts = true; |
|
576 else if ( strcmp( pt, "-check-outlines" ) == 0 ) |
|
577 check_outlines = true; |
|
578 else if ( strcmp( pt, "-dir" ) == 0 ) |
|
579 dirs[dcnt++] = argv[++i]; |
|
580 else if ( strcmp( pt, "-error-count" ) == 0 ) |
|
581 { |
|
582 if ( !rset ) |
|
583 error_fraction = 0; |
|
584 rset = true; |
|
585 error_count = strtol( argv[++i], &end, 10 ); |
|
586 if ( *end != '\0' ) |
|
587 { |
|
588 fprintf( stderr, "Bad value for error-count: %s\n", argv[i] ); |
|
589 exit( 1 ); |
|
590 } |
|
591 } |
|
592 else if ( strcmp( pt, "-error-fraction" ) == 0 ) |
|
593 { |
|
594 if ( !rset ) |
|
595 error_count = 0; |
|
596 rset = true; |
|
597 error_fraction = strtod( argv[++i], &end ); |
|
598 if ( *end != '\0' ) |
|
599 { |
|
600 fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] ); |
|
601 exit( 1 ); |
|
602 } |
|
603 } |
|
604 else if ( strcmp( pt, "-ext" ) == 0 ) |
|
605 exts[ecnt++] = argv[++i]; |
|
606 else if ( strcmp( pt, "-help" ) == 0 ) |
|
607 { |
|
608 usage( stdout, argv[0] ); |
|
609 exit( 0 ); |
|
610 } |
|
611 else if ( strcmp( pt, "-nohints" ) == 0 ) |
|
612 nohints = true; |
|
613 else if ( strcmp( pt, "-rasterize" ) == 0 ) |
|
614 rasterize = true; |
|
615 else if ( strcmp( pt, "-results" ) == 0 ) |
|
616 results_dir = argv[++i]; |
|
617 else if ( strcmp( pt, "-size" ) == 0 ) |
|
618 { |
|
619 font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 ); |
|
620 if ( *end != '\0' || font_size < 64 ) |
|
621 { |
|
622 fprintf( stderr, "Bad value for size: %s\n", argv[i] ); |
|
623 exit( 1 ); |
|
624 } |
|
625 } |
|
626 else if ( strcmp( pt, "-test" ) == 0 ) |
|
627 testfile = argv[++i]; |
|
628 else |
|
629 { |
|
630 usage( stderr, argv[0] ); |
|
631 exit( 1 ); |
|
632 } |
|
633 } |
|
634 |
|
635 if ( allexts ) |
|
636 exts = NULL; |
|
637 else if ( ecnt == 0 ) |
|
638 exts = default_ext_list; |
|
639 |
|
640 if ( dcnt == 0 ) |
|
641 dirs = default_dir_list; |
|
642 |
|
643 if ( testfile != NULL ) |
|
644 ExecuteTest( testfile ); /* This should never return */ |
|
645 |
|
646 time( &now ); |
|
647 srandom( now ); |
|
648 |
|
649 FindFonts( dirs, exts ); |
|
650 mkdir( results_dir, 0755 ); |
|
651 |
|
652 forever |
|
653 do_test(); |
|
654 |
|
655 return 0; |
|
656 } |
|
657 |
|
658 |
|
659 /* EOF */ |