ref: bdb56bba866ddd3532bce008156bd3763614aa0e
parent: 43a96eb26f28dfc78fd48cab34e90f6f468c42da
author: Werner Lemberg <[email protected]>
date: Tue Oct 13 07:51:13 EDT 2015
[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'.
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
2015-10-13 Werner Lemberg <[email protected]>
+ [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 <[email protected]>
+
[truetype] Refine some GX sanity tests.
Use the `gvar' table size instead of the remaining bytes in the
--- 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 <assert.h>
#include <stdint.h>
+#include <memory>
+#include <vector>
+
+
+using namespace std;
+
+
#include <ft2build.h>
#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<FT_MM_Var,
+ decltype ( free )*> variations( variations_ptr, free );
+ vector<FT_Fixed> 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 );
-#if 0
- FT_Glyph glyph;
+ // test MM interface only for a face without a selected instance
+ if ( instance_index == 0 )
+ setIntermediateAxis( face );
- if ( !FT_Get_Glyph( face->glyph, &glyph ) )
- FT_Done_Glyph( glyph );
+ // 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;
- FT_Outline* outline = &face->glyph->outline;
- FT_Matrix rot30 = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 };
+ // Rendering is the most expensive and the least interesting part.
+ //
+ // if ( FT_Render_Glyph( face->glyph, render_mode) )
+ // continue;
+ // FT_GlyphSlot_Embolden( face->glyph );
- FT_Outline_Transform( outline, &rot30 );
+#if 0
+ FT_Glyph glyph;
+ if ( !FT_Get_Glyph( face->glyph, &glyph ) )
+ FT_Done_Glyph( glyph );
- FT_BBox bbox;
+ FT_Outline* outline = &face->glyph->outline;
+ FT_Matrix rot30 = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 };
- FT_Outline_Get_BBox( outline, &bbox );
+ FT_Outline_Transform( outline, &rot30 );
+
+ FT_BBox bbox;
+ FT_Outline_Get_BBox( outline, &bbox );
#endif
- }
+ }
- FT_Done_Face( face );
+ FT_Done_Face( face );
+ }
}
return 0;