Commit 801e7bae3a31047d280c6c853c8046ea4f399eaf

Werner Lemberg 2009-03-11T06:47:49

Fix Savannah bug #25781. We now simply check for a valid `offset', no longer handling `delta = 1' specially. * src/sfnt/ttcmap.c (tt_cmap4_validate): Don't check `delta' for last segment. (tt_cmap4_set_range, tt_cmap4_char_map_linear, tt_cmap4_char_map_binary): Check offset.

diff --git a/ChangeLog b/ChangeLog
index 1a1dedc..32660ab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2009-03-11  Werner Lemberg  <wl@gnu.org>
 
+	Fix Savannah bug #25781.
+	We now simply check for a valid `offset', no longer handling `delta
+	= 1' specially.
+
+	* src/sfnt/ttcmap.c (tt_cmap4_validate): Don't check `delta' for
+	last segment.
+	(tt_cmap4_set_range, tt_cmap4_char_map_linear,
+	tt_cmap4_char_map_binary): Check offset.
+
+2009-03-11  Werner Lemberg  <wl@gnu.org>
+
 	* src/base/Jamfile: Fix handling of ftadvanc.c.
 	Reported by Oran Agra <oran@monfort.co.il>.
 
diff --git a/src/sfnt/ttcmap.c b/src/sfnt/ttcmap.c
index 67b6684..6830391 100644
--- a/src/sfnt/ttcmap.c
+++ b/src/sfnt/ttcmap.c
@@ -231,7 +231,7 @@
   /*   language    4              USHORT          Mac language code        */
   /*   keys        6              USHORT[256]     sub-header keys          */
   /*   subs        518            SUBHEAD[NSUBS]  sub-headers array        */
-  /*   glyph_ids   518+NSUB*8     USHORT[]        glyph id array           */
+  /*   glyph_ids   518+NSUB*8     USHORT[]        glyph ID array           */
   /*                                                                       */
   /* The `keys' table is used to map charcode high-bytes to sub-headers.   */
   /* The value of `NSUBS' is the number of sub-headers defined in the      */
@@ -282,7 +282,7 @@
     FT_UInt   n, max_subs;
     FT_Byte*  keys;                         /* keys table */
     FT_Byte*  subs;                         /* sub-headers */
-    FT_Byte*  glyph_ids;                    /* glyph id array */
+    FT_Byte*  glyph_ids;                    /* glyph ID array */
 
 
     if ( table + length > valid->limit || length < 6 + 512 )
@@ -605,14 +605,14 @@
   /*                                                  each segment; can be */
   /*                                                  zero                 */
   /*                                                                       */
-  /*   glyphIds      16+NUM_SEGS*8  USHORT[]          array of glyph id    */
+  /*   glyphIds      16+NUM_SEGS*8  USHORT[]          array of glyph ID    */
   /*                                                  ranges               */
   /*                                                                       */
   /* Character codes are modelled by a series of ordered (increasing)      */
   /* intervals called segments.  Each segment has start and end codes,     */
   /* provided by the `startCount' and `endCount' arrays.  Segments must    */
-  /* not be overlapping and the last segment should always contain the     */
-  /* `0xFFFF' endCount.                                                    */
+  /* not overlap, and the last segment should always contain the value     */
+  /* 0xFFFF for `endCount'.                                                */
   /*                                                                       */
   /* The fields `searchRange', `entrySelector' and `rangeShift' are better */
   /* ignored (they are traces of over-engineering in the TrueType          */
@@ -629,10 +629,10 @@
   /* the segment, and the value of `idDelta' is added to it.               */
   /*                                                                       */
   /*                                                                       */
-  /* Finally, note that certain fonts contain invalid charmaps that        */
-  /* contain end=0xFFFF, start=0xFFFF, delta=0x0001, offset=0xFFFF at the  */
-  /* of their charmaps (e.g. opens___.ttf which comes with OpenOffice.org) */
-  /* we need special code to deal with them correctly...                   */
+  /* Finally, note that a lot of fonts contain an invalid last segment,    */
+  /* where `start' and `end' are correctly set to 0xFFFF but both `delta'  */
+  /* and `offset' are incorrect (e.g., `opens___.ttf' which comes with     */
+  /* OpenOffice.org).  We need special code to deal with them correctly.   */
   /*                                                                       */
 
 #ifdef TT_CONFIG_CMAP_FORMAT_4
@@ -697,13 +697,22 @@
       p     += num_ranges * 2;
       offset = FT_PEEK_USHORT( p );
 
-      /* some fonts handle the last segment incorrectly; */
-      /* we have to catch it                             */
+      /* some fonts have an incorrect last segment; */
+      /* we have to catch it                        */
       if ( range_index     >= num_ranges - 1 &&
            cmap->cur_start == 0xFFFFU        &&
-           cmap->cur_end   == 0xFFFFU        &&
-           cmap->cur_delta == 0x1U           )
-        offset = 0;
+           cmap->cur_end   == 0xFFFFU        )
+      {
+        TT_Face   face  = (TT_Face)cmap->cmap.cmap.charmap.face;
+        FT_Byte*  limit = face->cmap_table + face->cmap_size;
+
+
+        if ( offset && p + offset + 2 > limit )
+        {
+          cmap->cur_delta = 1;
+          offset          = 0;
+        }
+      }
 
       if ( offset != 0xFFFFU )
       {
@@ -870,7 +879,7 @@
     offsets   = deltas  + num_segs * 2;
     glyph_ids = offsets + num_segs * 2;
 
-    /* check last segment, its end count must be 0xFFFF */
+    /* check last segment; its end count value must be 0xFFFF */
     if ( valid->level >= FT_VALIDATE_PARANOID )
     {
       p = ends + ( num_segs - 1 ) * 2;
@@ -899,10 +908,10 @@
         if ( start > end )
           FT_INVALID_DATA;
 
-        /* this test should be performed at default validation level;  */
-        /* unfortunately, some popular Asian fonts present overlapping */
-        /* ranges in their charmaps                                    */
-        /*                                                             */
+        /* this test should be performed at default validation level; */
+        /* unfortunately, some popular Asian fonts have overlapping   */
+        /* ranges in their charmaps                                   */
+        /*                                                            */
         if ( start <= last_end && n > 0 )
         {
           if ( valid->level >= FT_VALIDATE_TIGHT )
@@ -910,7 +919,7 @@
           else
           {
             /* allow overlapping segments, provided their start points */
-            /* and end points, respectively, are in ascending order.   */
+            /* and end points, respectively, are in ascending order    */
             /*                                                         */
             if ( last_start > start || last_end > end )
               error |= TT_CMAP_FLAG_UNSORTED;
@@ -921,7 +930,7 @@
 
         if ( offset && offset != 0xFFFFU )
         {
-          p += offset;  /* start of glyph id array */
+          p += offset;  /* start of glyph ID array */
 
           /* check that we point within the glyph IDs table only */
           if ( valid->level >= FT_VALIDATE_TIGHT )
@@ -930,11 +939,18 @@
                  p + ( end - start + 1 ) * 2 > table + length )
               FT_INVALID_DATA;
           }
-          /* some fonts handle the last segment incorrectly */
-          else if ( n != num_segs - 1     ||
-                    !( start == 0xFFFFU &&
-                       end   == 0xFFFFU &&
-                       delta == 0x1U    ) )
+          /* Some fonts handle the last segment incorrectly.  In */
+          /* theory, 0xFFFF might point to an ordinary glyph --  */
+          /* a cmap 4 is versatile and could be used for any     */
+          /* encoding, not only Unicode.  However, reality shows */
+          /* that far too many fonts are sloppy and incorrectly  */
+          /* set all fields but `start' and `end' for the last   */
+          /* segment if it contains only a single character.     */
+          /*                                                     */
+          /* We thus omit the test here, delaying it to the      */
+          /* routines which actually access the cmap.            */
+          else if ( n != num_segs - 1                       ||
+                    !( start == 0xFFFFU && end == 0xFFFFU ) )
           {
             if ( p < glyph_ids                              ||
                  p + ( end - start + 1 ) * 2 > valid->limit )
@@ -965,9 +981,9 @@
           /* some fonts (erroneously?) use a range offset of 0xFFFF */
           /* to mean missing glyph in cmap table                    */
           /*                                                        */
-          if ( valid->level >= FT_VALIDATE_PARANOID                     ||
-               n != num_segs - 1                                        ||
-               !( start == 0xFFFFU && end == 0xFFFFU && delta == 0x1U ) )
+          if ( valid->level >= FT_VALIDATE_PARANOID    ||
+               n != num_segs - 1                       ||
+               !( start == 0xFFFFU && end == 0xFFFFU ) )
             FT_INVALID_DATA;
         }
 
@@ -1025,11 +1041,21 @@
           p      += num_segs2;
           offset  = TT_PEEK_USHORT( p );
 
-          /* some fonts handle the last segment incorrectly; */
-          /* we have to catch it                             */
-          if ( i >= num_segs - 1                                   &&
-               start == 0xFFFFU && end == 0xFFFFU && delta == 0x1U )
-            offset = 0;
+          /* some fonts have an incorrect last segment; */
+          /* we have to catch it                        */
+          if ( i >= num_segs - 1                  &&
+               start == 0xFFFFU && end == 0xFFFFU )
+          {
+            TT_Face   face  = (TT_Face)cmap->cmap.charmap.face;
+            FT_Byte*  limit = face->cmap_table + face->cmap_size;
+
+
+            if ( offset && p + offset + 2 > limit )
+            {
+              delta  = 1;
+              offset = 0;
+            }
+          }
 
           if ( offset == 0xFFFFU )
             continue;
@@ -1110,11 +1136,21 @@
         p     += num_segs2;
         offset = TT_PEEK_USHORT( p );
 
-        /* some fonts handle the last segment incorrectly; */
-        /* we have to catch it                             */
-        if ( mid >= num_segs - 1                                 &&
-             start == 0xFFFFU && end == 0xFFFFU && delta == 0x1U )
-          offset = 0;
+        /* some fonts have an incorrect last segment; */
+        /* we have to catch it                        */
+        if ( mid >= num_segs - 1                &&
+             start == 0xFFFFU && end == 0xFFFFU )
+        {
+          TT_Face   face  = (TT_Face)cmap->cmap.charmap.face;
+          FT_Byte*  limit = face->cmap_table + face->cmap_size;
+
+
+          if ( offset && p + offset + 2 > limit )
+          {
+            delta  = 1;
+            offset = 0;
+          }
+        }
 
         /* search the first segment containing `charcode' */
         if ( cmap->flags & TT_CMAP_FLAG_OVERLAPPING )
@@ -1580,7 +1616,7 @@
   /*                                                                       */
   /*   start       0              ULONG       first charcode               */
   /*   end         4              ULONG       last charcode                */
-  /*   startId     8              ULONG       start glyph id for the group */
+  /*   startId     8              ULONG       start glyph ID for the group */
   /*                                                                       */
 
 #ifdef TT_CONFIG_CMAP_FORMAT_8
@@ -1962,7 +1998,7 @@
   /*                                                                       */
   /*   start       0          ULONG      first charcode                    */
   /*   end         4          ULONG      last charcode                     */
-  /*   startId     8          ULONG      start glyph id for the group      */
+  /*   startId     8          ULONG      start glyph ID for the group      */
   /*                                                                       */
 
 #ifdef TT_CONFIG_CMAP_FORMAT_12