Commit 6b19e095be24f993db7a2f4293915f007a91e4e8

Werner Lemberg 2008-07-09T06:20:12

* src/truetype/ttpload.c (tt_face_load_loca): Handle buggy fonts where num_locations < num_glyphs. Problem reported by Ding Li.

diff --git a/ChangeLog b/ChangeLog
index 3917ba1..4ca36a8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2008-07-09  Werner Lemberg  <wl@gnu.org>
+
+	* src/truetype/ttpload.c (tt_face_load_loca): Handle buggy fonts
+	where num_locations < num_glyphs.  Problem reported by Ding Li.
+
 2008-07-05  Werner Lemberg  <wl@gnu.org>
 
 	Since FreeType uses `$(value ...)', we now need GNU make 3.80 or
diff --git a/src/truetype/ttpload.c b/src/truetype/ttpload.c
index 9d3381b..520fc87 100644
--- a/src/truetype/ttpload.c
+++ b/src/truetype/ttpload.c
@@ -4,7 +4,7 @@
 /*                                                                         */
 /*    TrueType-specific tables loader (body).                              */
 /*                                                                         */
-/*  Copyright 1996-2001, 2002, 2004, 2005, 2006, 2007 by                   */
+/*  Copyright 1996-2001, 2002, 2004, 2005, 2006, 2007, 2008 by             */
 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
 /*                                                                         */
 /*  This file is part of the FreeType project, and may only be used,       */
@@ -65,6 +65,7 @@
   {
     FT_Error  error;
     FT_ULong  table_len;
+    FT_Int    shift;
 
 
     /* we need the size of the `glyf' table for malformed `loca' tables */
@@ -82,23 +83,65 @@
 
     if ( face->header.Index_To_Loc_Format != 0 )
     {
+      shift = 2;
+
       if ( table_len >= 0x40000L )
       {
         FT_TRACE2(( "table too large!\n" ));
         error = TT_Err_Invalid_Table;
         goto Exit;
       }
-      face->num_locations = (FT_UInt)( table_len >> 2 );
+      face->num_locations = (FT_UInt)( table_len >> shift );
     }
     else
     {
+      shift = 1;
+
       if ( table_len >= 0x20000L )
       {
         FT_TRACE2(( "table too large!\n" ));
         error = TT_Err_Invalid_Table;
         goto Exit;
       }
-      face->num_locations = (FT_UInt)( table_len >> 1 );
+      face->num_locations = (FT_UInt)( table_len >> shift );
+    }
+
+    if ( face->num_locations != (FT_UInt)face->root.num_glyphs )
+    {
+      FT_TRACE2(( "glyph count mismatch!  loca: %d, maxp: %d\n",
+                  face->num_locations, face->root.num_glyphs ));
+
+      /* we only handle the case where `maxp' gives a larger value */
+      if ( face->num_locations < (FT_UInt)face->root.num_glyphs )
+      {
+        FT_Long   new_loca_len = (FT_Long)face->root.num_glyphs << shift;
+
+        TT_Table  entry = face->dir_tables;
+        TT_Table  limit = entry + face->num_tables;
+
+        FT_Long   pos  = FT_Stream_Pos( stream );
+        FT_Long   dist = 0x7FFFFFFFL;
+
+
+        /* compute the distance to next table in font file */
+        for ( ; entry < limit; entry++ )
+        {
+          FT_Long  diff = entry->Offset - pos;
+
+
+          if ( diff > 0 && diff < dist )
+            dist = diff;
+        }
+
+        if ( new_loca_len <= dist )
+        {
+          face->num_locations = (FT_Long)face->root.num_glyphs;
+          table_len           = new_loca_len;
+
+          FT_TRACE2(( "adjusting num_locations to %d\n",
+                      face->num_locations ));
+        }
+      }
     }
 
     /*