Commit 27fee7f8c6ae75e066b5a23472b33925c6e8b1e0

Werner Lemberg 2017-03-06T20:45:44

[sfnt, truetype] Always provide default instance. As documented in the OpenType specification, an entry for the default instance may be omitted in the named instance table. In particular this means that even if there is no named instance table in the font we actually do have a named instance, namely the default instance. For consistency, we always want the default instance in our list of named instances. If it is missing, we try to synthesize it. * src/sfnt/sfobjs.c (sfnt_init_face): Check whether the default instance is in the table of named instances. Otherwise adjust number of instances. * src/truetype/ttgxvar.c: Include FT_TRUETYPE_IDS_H. (TT_Get_MM_Var): Use `face->root.style_flags' as the number of named instances. Sythesize a named instance entry if necessary. (tt_done_blend): Free `normalized_stylecoords'.

diff --git a/ChangeLog b/ChangeLog
index 3bbfe42..3074bcb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2017-03-06  Werner Lemberg  <wl@gnu.org>
+
+	[sfnt, truetype] Always provide default instance.
+
+	As documented in the OpenType specification, an entry for the
+	default instance may be omitted in the named instance table.  In
+	particular this means that even if there is no named instance table
+	in the font we actually do have a named instance, namely the default
+	instance.
+
+	For consistency, we always want the default instance in our list of
+	named instances.  If it is missing, we try to synthesize it.
+
+	* src/sfnt/sfobjs.c (sfnt_init_face): Check whether the default
+	instance is in the table of named instances.  Otherwise adjust
+	number of instances.
+
+	* src/truetype/ttgxvar.c: Include FT_TRUETYPE_IDS_H.
+	(TT_Get_MM_Var): Use `face->root.style_flags' as the number of named
+	instances.
+	Sythesize a named instance entry if necessary.
+	(tt_done_blend): Free `normalized_stylecoords'.
+
 2017-03-05  Werner Lemberg  <wl@gnu.org>
 
 	[sfnt] Remove redundant code.
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
index 4cc3e3f..51be1c2 100644
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -856,6 +856,7 @@
                   FT_Parameter*  params )
   {
     FT_Error      error;
+    FT_Memory     memory = face->root.memory;
     FT_Library    library = face->root.driver->root.library;
     SFNT_Service  sfnt;
     FT_Int        face_index;
@@ -954,6 +955,9 @@
 
       FT_Int  instance_index;
 
+      FT_Byte*  default_values  = NULL;
+      FT_Byte*  instance_values = NULL;
+
 
       face->is_default_instance = 1;
 
@@ -999,6 +1003,65 @@
       if ( !face->goto_table( face, TTAG_CFF, stream, 0 ) )
         num_instances = 0;
 
+      /*
+       *  As documented in the OpenType specification, an entry for the
+       *  default instance may be omitted in the named instance table.  In
+       *  particular this means that even if there is no named instance
+       *  table in the font we actually do have a named instance, namely the
+       *  default instance.
+       *
+       *  For consistency, we always want the default instance in our list
+       *  of named instances.  If it is missing, we try to synthesize it
+       *  later on.  Here, we have to adjust `num_instances' accordingly.
+       */
+
+      if ( !( FT_ALLOC( default_values, num_axes * 2 )  ||
+              FT_ALLOC( instance_values, num_axes * 2 ) ) )
+      {
+        /* the current stream position is 16 bytes after the table start */
+        FT_ULong  array_start = FT_STREAM_POS() - 16 + offset;
+        FT_ULong  default_value_offset, instance_offset;
+
+        FT_Byte*  p;
+        FT_UInt   i;
+
+
+        default_value_offset = array_start + 8;
+        p                    = default_values;
+
+        for ( i = 0; i < num_axes; i++ )
+        {
+          (void)FT_STREAM_READ_AT( default_value_offset, p, 2 );
+
+          default_value_offset += axis_size;
+          p                    += 2;
+        }
+
+        instance_offset = array_start + axis_size * num_axes + 4;
+
+        for ( i = 0; i < num_instances; i++ )
+        {
+          (void)FT_STREAM_READ_AT( instance_offset,
+                                   instance_values,
+                                   num_axes * 2 );
+
+          if ( !ft_memcmp( default_values, instance_values, num_axes * 2 ) )
+            break;
+
+          instance_offset += instance_size;
+        }
+
+        if ( i == num_instances )
+        {
+          /* no default instance in named instance table; */
+          /* we thus have to synthesize it                */
+          num_instances++;
+        }
+      }
+
+      FT_FREE( default_values );
+      FT_FREE( instance_values );
+
       /* instance indices in `face_instance_index' start with index 1, */
       /* thus `>' and not `>='                                         */
       if ( instance_index > num_instances )
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
index 50da2ea..fa614ca 100644
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -45,6 +45,7 @@
 #include FT_INTERNAL_STREAM_H
 #include FT_INTERNAL_SFNT_H
 #include FT_TRUETYPE_TAGS_H
+#include FT_TRUETYPE_IDS_H
 #include FT_MULTIPLE_MASTERS_H
 #include FT_LIST_H
 
@@ -1930,15 +1931,17 @@
     FT_ULong             table_len;
     FT_Error             error  = FT_Err_Ok;
     FT_ULong             fvar_start;
-    FT_Int               i, j;
+    FT_UInt              i, j;
     FT_MM_Var*           mmvar = NULL;
     FT_Fixed*            next_coords;
     FT_Fixed*            nsc;
     FT_String*           next_name;
     FT_Var_Axis*         a;
+    FT_Fixed*            c;
     FT_Var_Named_Style*  ns;
     GX_FVar_Head         fvar_head;
     FT_Bool              usePsName;
+    FT_UInt              num_instances;
 
     static const FT_Frame_Field  fvar_fields[] =
     {
@@ -2019,12 +2022,18 @@
       if ( FT_NEW( face->blend ) )
         goto Exit;
 
-      /* cannot overflow 32-bit arithmetic because of the validity check */
+      /* `num_instances' holds the number of all named instances, */
+      /* including the default instance which might be missing    */
+      /* in fvar's table of named instances                       */
+      num_instances = face->root.style_flags >> 16;
+
+      /* cannot overflow 32-bit arithmetic because of the size limits */
+      /* used in the `fvar' table validity check in `sfnt_init_face'  */
       face->blend->mmvar_len =
         sizeof ( FT_MM_Var ) +
         fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
-        fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
-        fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
+        num_instances * sizeof ( FT_Var_Named_Style ) +
+        num_instances * fvar_head.axisCount * sizeof ( FT_Fixed ) +
         5 * fvar_head.axisCount;
 
       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
@@ -2041,15 +2050,15 @@
                                /* may have a different number of designs  */
                                /* (or tuples, as called by Apple)         */
       mmvar->num_namedstyles =
-        fvar_head.instanceCount;
+        num_instances;
       mmvar->axis =
         (FT_Var_Axis*)&( mmvar[1] );
       mmvar->namedstyle =
         (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] );
 
       next_coords =
-        (FT_Fixed*)&( mmvar->namedstyle[fvar_head.instanceCount] );
-      for ( i = 0; i < fvar_head.instanceCount; i++ )
+        (FT_Fixed*)&( mmvar->namedstyle[num_instances] );
+      for ( i = 0; i < num_instances; i++ )
       {
         mmvar->namedstyle[i].coords  = next_coords;
         next_coords                 += fvar_head.axisCount;
@@ -2109,17 +2118,14 @@
 
       FT_TRACE5(( "\n" ));
 
-      if ( fvar_head.instanceCount )
-      {
-        /* named instance coordinates are stored as design coordinates; */
-        /* we have to convert them to normalized coordinates also       */
-        if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
-                           fvar_head.axisCount * fvar_head.instanceCount ) )
-          goto Exit;
+      /* named instance coordinates are stored as design coordinates; */
+      /* we have to convert them to normalized coordinates also       */
+      if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
+                         fvar_head.axisCount * num_instances ) )
+        goto Exit;
 
-        if ( !face->blend->avar_checked )
-          ft_var_load_avar( face );
-      }
+      if ( fvar_head.instanceCount && !face->blend->avar_checked )
+        ft_var_load_avar( face );
 
       ns  = mmvar->namedstyle;
       nsc = face->blend->normalized_stylecoords;
@@ -2133,8 +2139,9 @@
         ns->strid       =    FT_GET_USHORT();
         (void) /* flags = */ FT_GET_USHORT();
 
-        for ( j = 0; j < fvar_head.axisCount; j++ )
-          ns->coords[j] = FT_GET_LONG();
+        c = ns->coords;
+        for ( j = 0; j < fvar_head.axisCount; j++, c++ )
+          *c = FT_GET_LONG();
 
         if ( usePsName )
           ns->psid = FT_GET_USHORT();
@@ -2148,6 +2155,55 @@
         FT_FRAME_EXIT();
       }
 
+      if ( num_instances != fvar_head.instanceCount )
+      {
+        SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
+
+        FT_Int  found, win, apple;
+
+
+        /* the default instance is missing in array the   */
+        /* of named instances; try to synthesize an entry */
+        found = sfnt->get_name_id( face,
+                                   TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
+                                   &win,
+                                   &apple );
+        if ( !found )
+          found = sfnt->get_name_id( face,
+                                     TT_NAME_ID_FONT_SUBFAMILY,
+                                     &win,
+                                     &apple );
+
+        if ( found )
+        {
+          FT_Int  strid = win >= 0 ? win : apple;
+
+
+          found = sfnt->get_name_id( face,
+                                     TT_NAME_ID_PS_NAME,
+                                     &win,
+                                     &apple );
+          if ( found )
+          {
+            FT_Int  psid = win >= 0 ? win : apple;
+
+
+            FT_TRACE5(( "TT_Get_MM_Var:"
+                        " Adding default instance to named instances\n" ));
+
+            ns = &mmvar->namedstyle[fvar_head.instanceCount];
+
+            ns->strid = strid;
+            ns->psid  = psid;
+
+            a = mmvar->axis;
+            c = ns->coords;
+            for ( j = 0; j < fvar_head.axisCount; j++, a++, c++ )
+              *c = a->def;
+          }
+        }
+      }
+
       ft_var_load_mvar( face );
     }
 
@@ -3528,6 +3584,7 @@
       num_axes = blend->mmvar->num_axis;
 
       FT_FREE( blend->normalizedcoords );
+      FT_FREE( blend->normalized_stylecoords );
       FT_FREE( blend->mmvar );
 
       if ( blend->avar_segment )