Commit 2ff83a5c99abf3d72d6e7bf4db179b671c7d59cb

Werner Lemberg 2015-09-30T14:44:29

[sfnt] Rewrite `tt_cmap4_char_map_linear' (#46078). * src/sfnt/ttcmap.c (tt_cmap4_char_map_linear): Add code to better skip invalid segments. If searching the next character, provide a more efficient logic to speed up the code.

diff --git a/ChangeLog b/ChangeLog
index de74213..2786754 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2015-09-30  Werner Lemberg  <wl@gnu.org>
 
+	[sfnt] Rewrite `tt_cmap4_char_map_linear' (#46078).
+
+	* src/sfnt/ttcmap.c (tt_cmap4_char_map_linear): Add code to better
+	skip invalid segments.
+	If searching the next character, provide a more efficient logic to
+	speed up the code.
+
+2015-09-30  Werner Lemberg  <wl@gnu.org>
+
 	[truetype] Adjust number of glyphs for malformed `loca' tables.
 
 	* src/truetype/ttpload.c (tt_face_load_loca): Implement it.
diff --git a/src/sfnt/ttcmap.c b/src/sfnt/ttcmap.c
index 6acba73..e80fc54 100644
--- a/src/sfnt/ttcmap.c
+++ b/src/sfnt/ttcmap.c
@@ -1035,12 +1035,17 @@
                             FT_UInt32*  pcharcode,
                             FT_Bool     next )
   {
+    TT_Face   face  = (TT_Face)cmap->cmap.charmap.face;
+    FT_Byte*  limit = face->cmap_table + face->cmap_size;
+
+
     FT_UInt    num_segs2, start, end, offset;
     FT_Int     delta;
     FT_UInt    i, num_segs;
     FT_UInt32  charcode = *pcharcode;
     FT_UInt    gindex   = 0;
     FT_Byte*   p;
+    FT_Byte*   q;
 
 
     p = cmap->data + 6;
@@ -1054,65 +1059,104 @@
     if ( next )
       charcode++;
 
+    if ( charcode > 0xFFFFU )
+      return 0;
+
     /* linear search */
-    for ( ; charcode <= 0xFFFFU; charcode++ )
-    {
-      FT_Byte*  q;
+    p = cmap->data + 14;               /* ends table   */
+    q = cmap->data + 16 + num_segs2;   /* starts table */
 
+    for ( i = 0; i < num_segs; i++ )
+    {
+      end   = TT_NEXT_USHORT( p );
+      start = TT_NEXT_USHORT( q );
 
-      p = cmap->data + 14;               /* ends table   */
-      q = cmap->data + 16 + num_segs2;   /* starts table */
+      if ( charcode < start )
+      {
+        if ( next )
+          charcode = start;
+        else
+          break;
+      }
 
-      for ( i = 0; i < num_segs; i++ )
+    Again:
+      if ( charcode <= end )
       {
-        end   = TT_NEXT_USHORT( p );
-        start = TT_NEXT_USHORT( q );
+        FT_Byte*  r;
 
-        if ( charcode >= start && charcode <= end )
+
+        r       = q - 2 + num_segs2;
+        delta   = TT_PEEK_SHORT( r );
+        r      += num_segs2;
+        offset  = TT_PEEK_USHORT( r );
+
+        /* some fonts have an incorrect last segment; */
+        /* we have to catch it                        */
+        if ( i >= num_segs - 1                  &&
+             start == 0xFFFFU && end == 0xFFFFU )
         {
-          p       = q - 2 + num_segs2;
-          delta   = TT_PEEK_SHORT( p );
-          p      += num_segs2;
-          offset  = TT_PEEK_USHORT( p );
-
-          /* some fonts have an incorrect last segment; */
-          /* we have to catch it                        */
-          if ( i >= num_segs - 1                  &&
-               start == 0xFFFFU && end == 0xFFFFU )
+          if ( offset && r + offset + 2 > limit )
           {
-            TT_Face   face  = (TT_Face)cmap->cmap.charmap.face;
-            FT_Byte*  limit = face->cmap_table + face->cmap_size;
+            delta  = 1;
+            offset = 0;
+          }
+        }
 
+        if ( offset == 0xFFFFU )
+          continue;
 
-            if ( offset && p + offset + 2 > limit )
-            {
-              delta  = 1;
-              offset = 0;
-            }
-          }
+        if ( offset )
+        {
+          r += offset + ( charcode - start ) * 2;
 
-          if ( offset == 0xFFFFU )
+          /* if r > limit, the whole segment is invalid */
+          if ( next && r > limit )
             continue;
 
-          if ( offset )
+          gindex = TT_PEEK_USHORT( r );
+          if ( gindex )
+            gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU;
+        }
+        else
+        {
+          gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU;
+
+          if ( next && gindex >= (FT_UInt)face->root.num_glyphs )
           {
-            p += offset + ( charcode - start ) * 2;
-            gindex = TT_PEEK_USHORT( p );
-            if ( gindex != 0 )
-              gindex = (FT_UInt)( (FT_Int)gindex + delta ) & 0xFFFFU;
+            /* we have an invalid glyph index; if there is an overflow, */
+            /* we can adjust `charcode', otherwise the whole segment is */
+            /* invalid                                                  */
+            if ( (FT_Int)charcode + delta < 0 &&
+                 (FT_Int)end + delta >= 0     )
+            {
+              charcode = (FT_UInt)( -delta );
+              gindex   = 0;
+            }
+            else if ( (FT_Int)charcode + delta < 0x10000L &&
+                      (FT_Int)end + delta >= 0x10000L     )
+            {
+              charcode = (FT_UInt)( 0x10000L - delta );
+              gindex   = 0;
+            }
+            else
+              continue;
           }
-          else
-            gindex = (FT_UInt)( (FT_Int)charcode + delta ) & 0xFFFFU;
+        }
 
-          break;
+        if ( next && !gindex )
+        {
+          if ( charcode >= 0xFFFFU )
+            break;
+
+          charcode++;
+          goto Again;
         }
-      }
 
-      if ( !next || gindex )
         break;
+      }
     }
 
-    if ( next && gindex )
+    if ( next )
       *pcharcode = charcode;
 
     return gindex;