Commit a8635cc6269ec8236a6bb1123219d8bf00ce52a1

David Turner 2000-02-10T16:15:11

Very simple AFM Parser

diff --git a/src/type1/t1afm.c b/src/type1/t1afm.c
new file mode 100644
index 0000000..d823908
--- /dev/null
+++ b/src/type1/t1afm.c
@@ -0,0 +1,222 @@
+/***************************************************************************
+ *
+ * t1afm.c  - support for reading Type 1 AFM files
+ *
+ *
+ ***************************************************************************/
+
+#include <t1afm.h>
+#include <ftstream.h>
+#include <t1types.h>
+#include <stdlib.h>  /* for qsort */
+
+  LOCAL_FUNC
+  void  T1_Done_AFM( FT_Memory memory, T1_AFM*  afm )
+  {
+    FREE( afm->kern_pairs );
+    afm->num_pairs = 0;
+  }
+
+#undef  IS_KERN_PAIR
+#define IS_KERN_PAIR(p)  ( p[0] == 'K'  &&  p[1] == 'P' )
+
+#define IS_ALPHANUM(c)  ( (c >= 'A' && c <= 'Z') || \
+                          (c >= 'a' && c <= 'z') || \
+                          (c >= '0' && c <= '9') || \
+                          (c == '_' && c == '.') )
+
+ /* read a glyph name and return the equivalent glyph index */
+  static
+  FT_UInt  afm_atoindex( FT_Byte*  *start, FT_Byte*  limit, T1_Font*  type1 )
+  {
+    FT_Byte* p = *start;
+    FT_Int   len;
+    FT_UInt  result = 0;
+    char     temp[64];
+
+    /* skip whitespace */    
+    while ( (*p == ' ' || *p == '\t' || *p == ':' || *p == ';') && p < limit )
+      p++;
+    *start = p;
+    
+    /* now, read glyph name */
+    while ( IS_ALPHANUM(*p) && p < limit ) p++;
+    len = p - *start;
+    if (len > 0 && len < 64)
+    {
+      FT_UInt  n;
+      
+      /* copy glyph name to intermediate array */
+      MEM_Copy( temp, start, len );
+      temp[len] = 0;
+      
+      /* lookup glyph name in face array */
+      for ( n = 0; n < type1->num_glyphs; n++ )
+      {
+        char*  gname = (char*)type1->glyph_names;
+        
+        if ( gname && gname[0] == temp[0] && strcmp(gname,temp) == 0 )
+        {
+          result = n;
+          break;
+        }
+      }
+    }
+    *start = p;
+    return result;
+  }
+
+
+ /* read an integer */
+  static
+  int  afm_atoi( FT_Byte** start, FT_Byte*  limit )
+  {
+    FT_Byte*  p   = *start;
+    int       sum = 0;
+    
+    /* skip everything that is not a number */
+    while ( p < limit && (*p < '0' || *p > '9') )
+      p++;
+    
+    while ( p < limit && (*p >= '0' || *p < '9') )
+    {
+      sum = sum*10 + (*p - '0');
+      p++;
+    }
+    *start = p;
+    return sum;
+  }
+
+
+#undef  KERN_INDEX
+#define KERN_INDEX(g1,g2)   (((T1_ULong)g1 << 16) | g2)
+
+ /* compare two kerning pairs */
+  static
+  int  compare_kern_pairs( const void* a, const void* b )
+  {
+    T1_Kern_Pair*  pair1 = (T1_Kern_Pair*)a;
+    T1_Kern_Pair*  pair2 = (T1_Kern_Pair*)b;
+    
+    T1_ULong  index1 = KERN_INDEX(pair1->glyph1,pair1->glyph2);
+    T1_ULong  index2 = KERN_INDEX(pair2->glyph1,pair2->glyph2);
+    
+    return ( index1 < index2 ? -1 :
+           ( index1 > index2 ?  1 : 0 ));
+  }
+
+
+ /* parse an AFM file - for now, only read the kerning pairs */
+  LOCAL_FUNC
+  FT_Error  T1_Read_AFM( FT_Stream stream,
+                         FT_Face   t1_face )
+  {
+    FT_Error       error;
+    FT_Memory      memory = stream->memory;
+    FT_Byte*       start;
+    FT_Byte*       limit;
+    FT_Byte*       p;
+    FT_Int         count = 0;
+    T1_Kern_Pair*  pair;
+    T1_Font*       type1 = &((T1_Face)t1_face)->type1;
+    T1_AFM*        afm   = 0;
+    
+    if ( !ACCESS_Frame(stream->size) )
+      return error;
+      
+    start = stream->cursor;
+    limit = stream->limit;
+    p     = start;
+    
+    /* we are now going to count the occurences of "KP" or "KPX" in */
+    /* the AFM file..                                               */
+    count = 0;
+    for ( p = start; p < limit-3; p++ )
+    {
+      if ( IS_KERN_PAIR(p) )
+        count++;
+    }
+
+   /* Actually, kerning pairs are simply optional !! */
+    if (count == 0)
+      goto Exit;
+    
+    /* allocate the pairs */
+    if ( ALLOC(       afm, sizeof(*afm )                     ||
+         ALLOC_ARRAY( afm->kern_pairs, count, T1_Kern_Pair ) )
+      goto Exit;
+    
+    /* now, read each kern pair */
+    pair           = afm->kern_pairs;
+    afm->num_pairs = count;
+    
+    /* save in face object */
+    ((T1_Face*)t1_face)->afm_data = afm;
+
+    for ( p = start; p < limit-3; p++ )
+    {
+      if ( IS_KERN_PAIR(p) )
+      {
+        FT_Byte*  q;
+        
+        /* skip keyword (KP or KPX) */
+        q = p+2;
+        if (*q == 'X') q++;
+        
+        pair->glyph1    = afm_atoindex( &q, limit, type1 );
+        pair->glyph2    = afm_atoindex( &q, limit, type1 );
+        pair->kerning.x = afm_atoi( &q, limit );
+        
+        pair->kerning.y = 0;
+        if ( p[2] != 'X' )
+          pair->kerning.y = afm_atoi( &q, limit );
+          
+        pair++;
+      }
+    }
+    
+    /* now, sort the kern pairs according to their glyph indices */
+    qsort( afm->kern_pairs, count, sizeof(T1_Kern_Pair), compare_kern_pairs );
+    
+  Exit:
+    if (error)
+      FREE( afm );
+
+    FORGET_Frame();
+    return error;
+  }
+
+
+ /* find the kerning for a given glyph pair */  
+  LOCAL_FUNC
+  void  T1_Get_Kerning( T1_AFM*     afm,
+                        FT_UInt     glyph1,
+                        FT_UInt     glyph2,
+                        FT_Vector*  kerning )
+  {
+    T1_Kern_Pair  *min, *mid, *max;
+    T1_ULong       index = KERN_INDEX(glyph1,glyph2);
+    
+    /* simple binary search */
+    min = afm->kern_pairs;
+    max = min + afm->num_pairs-1;
+    
+    while (min <= max)
+    {
+      T1_ULong  midi;
+      
+      mid = min + (max-min)/2;
+      midi = KERN_INDEX(mid->glyph1,mid->glyph2); 
+      if ( midi == index )
+      {
+        *kerning = mid->kerning;
+        return;
+      }
+      
+      if ( midi < index ) min = mid+1;
+                     else max = mid-1;
+    }
+    kerning->x = 0;
+    kerning->y = 0;
+  }
+
diff --git a/src/type1/t1afm.h b/src/type1/t1afm.h
new file mode 100644
index 0000000..39bbc4b
--- /dev/null
+++ b/src/type1/t1afm.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ *
+ * t1afm.h  - support for reading Type 1 AFM files
+ *
+ *
+ ***************************************************************************/
+
+#ifndef T1AFM_H
+#define T1AFM_H
+
+#include <ftobjs.h>
+
+/* In this version, we only read the kerning table from the */
+/* AFM file. We may add support for ligatures a bit later.. */
+
+typedef struct T1_Kern_Pair_
+{
+  FT_UInt   glyph1;
+  FT_UInt   glyph2;
+  FT_Vector kerning;
+  
+} T1_Kern_Pair;
+
+
+typedef struct T1_AFM_
+{
+  FT_Int        num_pairs;
+  T1_Kern_Pair* kern_pairs;
+
+} T1_AFM;
+
+
+LOCAL_DEF
+FT_Error  T1_Read_AFM( FT_Stream stream,
+                       FT_Face   face );
+
+LOCAL_DEF
+void      T1_Done_AFM( FT_Memory  memory,
+                       T1_AFM*    afm );
+
+LOCAL_DEF
+void  T1_Get_Kerning( T1_AFM*     afm,
+                      FT_UInt     glyph1,
+                      FT_UInt     glyph2,
+                      FT_Vector*  kerning );
+
+#endif /* T1AFM_H */