diff --git a/ChangeLog b/ChangeLog index 2163162dc..b8a99e2ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2015-10-13 Werner Lemberg + + [ftfuzzer] Handle TTCs and MM/GX variations. + + This patch also contains various other improvements. + + * src/tools/ftfuzzer/ftfuzzer.cc: Add preprocessor guard to reject + pre-C++11 compilers. + (FT_Global): New class. Use it to provide a global constructor and + destructor for the `FT_Library' object. + (setIntermediateAxis): New function to select an (arbitrary) + instance. + (LLVMFuzzerTestOneInput): Loop over all faces and named instances. + Also call `FT_Set_Char_Size'. + 2015-10-13 Werner Lemberg [truetype] Refine some GX sanity tests. diff --git a/src/tools/ftfuzzer/ftfuzzer.cc b/src/tools/ftfuzzer/ftfuzzer.cc index 7b7197351..c4f5977ad 100644 --- a/src/tools/ftfuzzer/ftfuzzer.cc +++ b/src/tools/ftfuzzer/ftfuzzer.cc @@ -1,6 +1,18 @@ +// we use `unique_ptr' and `decltype', defined since C++11 +#if __cplusplus < 201103L +# error "a C++11 compiler is needed" +#endif + #include #include +#include +#include + + +using namespace std; + + #include #include FT_FREETYPE_H @@ -16,60 +28,147 @@ #include FT_MODULE_H #include FT_CFF_DRIVER_H #include FT_TRUETYPE_DRIVER_H +#include FT_MULTIPLE_MASTERS_H static FT_Library library; - static int InitResult = FT_Init_FreeType( &library ); + static int InitResult; + + struct FT_Global { + FT_Global() { + InitResult = FT_Init_FreeType( &library ); + } + ~FT_Global() { + FT_Done_FreeType( library ); + } + }; + + FT_Global global_ft; + + + static void + setIntermediateAxis( FT_Face face ) + { + // only handle Multiple Masters and GX variation fonts + if ( !( face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) ) + return; + + // get variation data for current instance + FT_MM_Var* variations_ptr = nullptr; + if ( FT_Get_MM_Var( face, &variations_ptr ) ) + return; + + unique_ptr variations( variations_ptr, free ); + vector coords( variations->num_axis ); + + // select an arbitrary instance + for ( unsigned int i = 0; i < variations->num_axis; i++ ) + coords[i] = ( variations->axis[i].minimum + + variations->axis[i].def ) / 2; + + if ( FT_Set_Var_Design_Coordinates( face, + coords.size(), + coords.data() ) ) + return; + } extern "C" int LLVMFuzzerTestOneInput( const uint8_t* data, - size_t size ) + size_t size_ ) { assert( !InitResult ); - if ( size < 1 ) + if ( size_ < 1 ) return 0; + long size = (long)size_; + FT_Face face; FT_Int32 load_flags = FT_LOAD_DEFAULT; +#if 0 FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; +#endif - if ( !FT_New_Memory_Face( library, data, size, 0, &face ) ) + // We use a conservative approach here, at the cost of calling + // `FT_New_Face' quite often. The idea is that the fuzzer should be + // able to try all faces and named instances of a font, expecting that + // some faces don't work for various reasons, e.g., a broken subfont, or + // an unsupported NFNT bitmap font in a Mac dfont resource that holds + // more than a single font. + + // get number of faces + if ( FT_New_Memory_Face( library, data, size, -1, &face ) ) + return 0; + long num_faces = face->num_faces; + FT_Done_Face( face ); + + // loop over all faces + for ( long face_index = 0; + face_index < num_faces; + face_index++ ) { - unsigned int first_index = 0; + // get number of instances + if ( FT_New_Memory_Face( library, + data, + size, + -( face_index + 1 ), + &face ) ) + continue; + long num_instances = face->style_flags >> 16; + FT_Done_Face( face ); - for ( unsigned i = first_index; - i < (unsigned int)face->num_glyphs; - i++ ) + // load face with and without instances + for ( long instance_index = 0; + instance_index < num_instances + 1; + instance_index++ ) { - if ( FT_Load_Glyph( face, i, load_flags ) ) + if ( FT_New_Memory_Face( library, + data, + size, + ( instance_index << 16 ) + face_index, + &face ) ) continue; - // Rendering is the most expensive and the least interesting part. - // - // if ( FT_Render_Glyph( face->glyph, render_mode) ) - // continue; - // FT_GlyphSlot_Embolden( face->glyph ); + // set up 20pt at 72dpi as an arbitrary size + FT_Set_Char_Size( face, 20, 20, 72, 72 ); + + // test MM interface only for a face without a selected instance + if ( instance_index == 0 ) + setIntermediateAxis( face ); + + // loop over all glyphs + for ( unsigned int glyph_index = 0; + glyph_index < (unsigned int)face->num_glyphs; + glyph_index++ ) + { + if ( FT_Load_Glyph( face, glyph_index, load_flags ) ) + continue; + + // Rendering is the most expensive and the least interesting part. + // + // if ( FT_Render_Glyph( face->glyph, render_mode) ) + // continue; + // FT_GlyphSlot_Embolden( face->glyph ); #if 0 - FT_Glyph glyph; + FT_Glyph glyph; + if ( !FT_Get_Glyph( face->glyph, &glyph ) ) + FT_Done_Glyph( glyph ); - if ( !FT_Get_Glyph( face->glyph, &glyph ) ) - FT_Done_Glyph( glyph ); + FT_Outline* outline = &face->glyph->outline; + FT_Matrix rot30 = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 }; - FT_Outline* outline = &face->glyph->outline; - FT_Matrix rot30 = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 }; + FT_Outline_Transform( outline, &rot30 ); - FT_Outline_Transform( outline, &rot30 ); - - FT_BBox bbox; - - FT_Outline_Get_BBox( outline, &bbox ); + FT_BBox bbox; + FT_Outline_Get_BBox( outline, &bbox ); #endif - } + } - FT_Done_Face( face ); + FT_Done_Face( face ); + } } return 0;