Commit 939df4207204625d207158de810826b3b034cbd5

Werner Lemberg 2017-01-26T21:41:38

[sfnt] Support `name' table format 1. * include/freetype/internal/tttypes.h (TT_LangTagRec): New structure. (TT_NameTableRec): Add fields `numLangTagRecords' and `langTags'. * src/sfnt/ttload.c (tt_face_load_name): Add support for language tags. Reduce array size of name strings in case of invalid entries. (tt_face_free_name): Updated. * docs/CHANGES: Updated.

diff --git a/ChangeLog b/ChangeLog
index bd45d75..7f4017e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2017-01-26  Werner Lemberg  <wl@gnu.org>
+
+	[sfnt] Support `name' table format 1.
+
+	* include/freetype/internal/tttypes.h (TT_LangTagRec): New
+	structure.
+	(TT_NameTableRec): Add fields `numLangTagRecords' and `langTags'.
+
+	* src/sfnt/ttload.c (tt_face_load_name): Add support for language
+	tags.
+	Reduce array size of name strings in case of invalid entries.
+	(tt_face_free_name): Updated.
+
+	* docs/CHANGES: Updated.
+
 2017-01-25  Werner Lemberg  <wl@gnu.org>
 
 	[sfnt] s/TT_NameEntry/TT_Name/.
diff --git a/docs/CHANGES b/docs/CHANGES
index 2f60cf5..0dec770 100644
--- a/docs/CHANGES
+++ b/docs/CHANGES
@@ -39,6 +39,13 @@ CHANGES BETWEEN 2.7.1 and 2.7.2
 
       The old macro names are deprecated (but still available).
 
+    - Support for SFNT `name' tables has been improved.
+
+      . Format 1 `name' tables are now supported.
+
+      . Language ID and name ID values have been updated to OpenType version
+        1.8.1.
+
 
 ======================================================================
 
diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h
index d6cf934..e779647 100644
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -279,7 +279,7 @@ FT_BEGIN_HEADER
     /* this last field is not defined in the spec */
     /* but used by the FreeType engine            */
 
-    FT_Byte*   string;
+    FT_Byte*  string;
 
   } TT_NameRec, *TT_Name;
 
@@ -287,30 +287,66 @@ FT_BEGIN_HEADER
   /*************************************************************************/
   /*                                                                       */
   /* <Struct>                                                              */
+  /*    TT_LangTagRec                                                      */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    A structure modeling language tag records in SFNT `name' tables,   */
+  /*    introduced in OpenType version 1.6.                                */
+  /*                                                                       */
+  /* <Fields>                                                              */
+  /*    stringLength :: The length of the string in bytes.                 */
+  /*                                                                       */
+  /*    stringOffset :: The offset to the string in the `name' table.      */
+  /*                                                                       */
+  /*    string       :: A pointer to the string's bytes.  Note that these  */
+  /*                    are UTF-16BE encoded characters.                   */
+  /*                                                                       */
+  typedef struct TT_LangTagRec_
+  {
+    FT_UShort  stringLength;
+    FT_ULong   stringOffset;
+
+    /* this last field is not defined in the spec */
+    /* but used by the FreeType engine            */
+
+    FT_Byte*  string;
+
+  } TT_LangTagRec, *TT_LangTag;
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
   /*    TT_NameTableRec                                                    */
   /*                                                                       */
   /* <Description>                                                         */
   /*    A structure modeling the TrueType name table.                      */
   /*                                                                       */
   /* <Fields>                                                              */
-  /*    format         :: The format of the name table.                    */
+  /*    format            :: The format of the name table.                 */
+  /*                                                                       */
+  /*    numNameRecords    :: The number of names in table.                 */
+  /*                                                                       */
+  /*    storageOffset     :: The offset of the name table in the `name'    */
+  /*                         TrueType table.                               */
   /*                                                                       */
-  /*    numNameRecords :: The number of names in table.                    */
+  /*    names             :: An array of name records.                     */
   /*                                                                       */
-  /*    storageOffset  :: The offset of the name table in the `name'       */
-  /*                      TrueType table.                                  */
+  /*    numLangTagRecords :: The number of language tags in table.         */
   /*                                                                       */
-  /*    names          :: An array of name records.                        */
+  /*    langTags          :: An array of language tag records.             */
   /*                                                                       */
-  /*    stream         :: the file's input stream.                         */
+  /*    stream            :: The file's input stream.                      */
   /*                                                                       */
   typedef struct  TT_NameTableRec_
   {
-    FT_UShort    format;
-    FT_UInt      numNameRecords;
-    FT_UInt      storageOffset;
-    TT_NameRec*  names;
-    FT_Stream    stream;
+    FT_UShort       format;
+    FT_UInt         numNameRecords;
+    FT_UInt         storageOffset;
+    TT_NameRec*     names;
+    FT_UInt         numLangTagRecords;
+    TT_LangTagRec*  langTags;
+    FT_Stream       stream;
 
   } TT_NameTableRec, *TT_NameTable;
 
diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c
index 3ab211c..4695253 100644
--- a/src/sfnt/ttload.c
+++ b/src/sfnt/ttload.c
@@ -808,7 +808,6 @@
     FT_Memory     memory = stream->memory;
     FT_ULong      table_pos, table_len;
     FT_ULong      storage_start, storage_limit;
-    FT_UInt       count;
     TT_NameTable  table;
 
     static const FT_Frame_Field  name_table_fields[] =
@@ -838,6 +837,17 @@
       FT_FRAME_END
     };
 
+    static const FT_Frame_Field  langTag_record_fields[] =
+    {
+#undef  FT_STRUCTURE
+#define FT_STRUCTURE  TT_LangTagRec
+
+      /* no FT_FRAME_START */
+        FT_FRAME_USHORT( stringLength ),
+        FT_FRAME_USHORT( stringOffset ),
+      FT_FRAME_END
+    };
+
 
     table         = &face->name_table;
     table->stream = stream;
@@ -848,18 +858,17 @@
 
     table_pos = FT_STREAM_POS();
 
-
     if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
       goto Exit;
 
-    /* Some popular Asian fonts have an invalid `storageOffset' value   */
-    /* (it should be at least "6 + 12*num_names").  However, the string */
-    /* offsets, computed as "storageOffset + entry->stringOffset", are  */
-    /* valid pointers within the name table...                          */
-    /*                                                                  */
-    /* We thus can't check `storageOffset' right now.                   */
-    /*                                                                  */
-    storage_start = table_pos + 6 + 12*table->numNameRecords;
+    /* Some popular Asian fonts have an invalid `storageOffset' value (it */
+    /* should be at least `6 + 12*numNameRecords').  However, the string  */
+    /* offsets, computed as `storageOffset + entry->stringOffset', are    */
+    /* valid pointers within the name table...                            */
+    /*                                                                    */
+    /* We thus can't check `storageOffset' right now.                     */
+    /*                                                                    */
+    storage_start = table_pos + 6 + 12 * table->numNameRecords;
     storage_limit = table_pos + table_len;
 
     if ( storage_start > storage_limit )
@@ -869,18 +878,56 @@
       goto Exit;
     }
 
-    /* Allocate the array of name records. */
-    count                 = table->numNameRecords;
-    table->numNameRecords = 0;
+    /* `name' format 1 contains additional language tag records, */
+    /* which we load first                                       */
+    if ( table->format == 1 )
+    {
+      if ( FT_STREAM_SEEK( storage_start )            ||
+           FT_READ_USHORT( table->numLangTagRecords ) )
+        goto Exit;
+
+      storage_start += 2 + 4 * table->numLangTagRecords;
+
+      /* allocate language tag records array */
+      if ( FT_NEW_ARRAY( table->langTags, table->numLangTagRecords ) ||
+           FT_FRAME_ENTER( table->numLangTagRecords * 4 )            )
+        goto Exit;
+
+      /* load language tags */
+      {
+        TT_LangTag  entry = table->langTags;
+        TT_LangTag  limit = entry + table->numLangTagRecords;
+
+
+        for ( ; entry < limit; entry++ )
+        {
+          (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
+
+          /* check that the langTag string is within the table */
+          entry->stringOffset += table_pos + table->storageOffset;
+          if ( entry->stringOffset                       < storage_start ||
+               entry->stringOffset + entry->stringLength > storage_limit )
+          {
+            /* invalid entry; ignore it */
+            entry->stringLength = 0;
+          }
+        }
+      }
+
+      FT_FRAME_EXIT();
+
+      (void)FT_STREAM_SEEK( table_pos + 6 );
+    }
 
-    if ( FT_NEW_ARRAY( table->names, count ) ||
-         FT_FRAME_ENTER( count * 12 )        )
+    /* allocate name records array */
+    if ( FT_NEW_ARRAY( table->names, table->numNameRecords ) ||
+         FT_FRAME_ENTER( table->numNameRecords * 12 )        )
       goto Exit;
 
-    /* Load the name records and determine how much storage is needed */
-    /* to hold the strings themselves.                                */
+    /* load name records */
     {
       TT_Name  entry = table->names;
+      FT_UInt  count = table->numNameRecords;
 
 
       for ( ; count > 0; count-- )
@@ -897,22 +944,37 @@
         if ( entry->stringOffset                       < storage_start ||
              entry->stringOffset + entry->stringLength > storage_limit )
         {
-          /* invalid entry - ignore it */
-          entry->stringOffset = 0;
-          entry->stringLength = 0;
+          /* invalid entry; ignore it */
           continue;
         }
 
+        /* assure that we have a valid language tag ID, and   */
+        /* that the corresponding langTag entry is valid, too */
+        if ( table->format == 1 && entry->languageID >= 0x8000U )
+        {
+          if ( entry->languageID - 0x8000U >= table->numLangTagRecords    ||
+               !table->langTags[entry->languageID - 0x8000U].stringLength )
+          {
+            /* invalid entry; ignore it */
+            continue;
+          }
+        }
+
         entry++;
       }
 
-      table->numNameRecords = (FT_UInt)( entry - table->names );
+      /* reduce array size to the actually used elements */
+      count = (FT_UInt)( entry - table->names );
+      (void)FT_RENEW_ARRAY( table->names,
+                            table->numNameRecords,
+                            count );
+      table->numNameRecords = count;
     }
 
     FT_FRAME_EXIT();
 
     /* everything went well, update face->num_names */
-    face->num_names = (FT_UShort) table->numNameRecords;
+    face->num_names = (FT_UShort)table->numNameRecords;
 
   Exit:
     return error;
@@ -935,25 +997,36 @@
   {
     FT_Memory     memory = face->root.driver->root.memory;
     TT_NameTable  table  = &face->name_table;
-    TT_Name       entry  = table->names;
-    FT_UInt       count  = table->numNameRecords;
 
 
     if ( table->names )
     {
-      for ( ; count > 0; count--, entry++ )
-      {
+      TT_Name  entry = table->names;
+      TT_Name  limit = entry + table->numNameRecords;
+
+
+      for ( ; entry < limit; entry++ )
         FT_FREE( entry->string );
-        entry->stringLength = 0;
-      }
 
-      /* free strings table */
       FT_FREE( table->names );
     }
 
-    table->numNameRecords = 0;
-    table->format         = 0;
-    table->storageOffset  = 0;
+    if ( table->langTags )
+    {
+      TT_LangTag  entry = table->langTags;
+      TT_LangTag  limit = entry + table->numLangTagRecords;
+
+
+      for ( ; entry < limit; entry++ )
+        FT_FREE( entry->string );
+
+      FT_FREE( table->langTags );
+    }
+
+    table->numNameRecords    = 0;
+    table->numLangTagRecords = 0;
+    table->format            = 0;
+    table->storageOffset     = 0;
   }