Commit bdb56bba866ddd3532bce008156bd3763614aa0e

Werner Lemberg 2015-10-13T11:51:13

[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'.

diff --git a/ChangeLog b/ChangeLog
index 2163162..b8a99e2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2015-10-13  Werner Lemberg  <wl@gnu.org>
 
+	[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  <wl@gnu.org>
+
 	[truetype] Refine some GX sanity tests.
 
 	Use the `gvar' table size instead of the remaining bytes in the
diff --git a/src/tools/ftfuzzer/ftfuzzer.cc b/src/tools/ftfuzzer/ftfuzzer.cc
index 7b71973..c4f5977 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 <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
+
+    // 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.
 
-    if ( !FT_New_Memory_Face( library, data, size, 0, &face ) )
+    // 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 );
+
+        // 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;
 
-        if ( !FT_Get_Glyph( face->glyph, &glyph ) )
-          FT_Done_Glyph( glyph );
+          // 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*  outline = &face->glyph->outline;
-        FT_Matrix    rot30   = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 };
+#if 0
+          FT_Glyph  glyph;
+          if ( !FT_Get_Glyph( face->glyph, &glyph ) )
+            FT_Done_Glyph( glyph );
 
-        FT_Outline_Transform( outline, &rot30 );
+          FT_Outline*  outline = &face->glyph->outline;
+          FT_Matrix    rot30   = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 };
 
-        FT_BBox  bbox;
+          FT_Outline_Transform( outline, &rot30 );
 
-        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;