shithub: freetype+ttf2subf

Download patch

ref: d25ad56d787cd4ecb37a69c64bfb2714458df7fc
parent: d393ca373812bd597651fc6f57dc5f6d58f8e7e1
author: David Turner <[email protected]>
date: Thu Oct 2 17:07:10 EDT 2003

* src/autofit/*: adding first sources of the new multi-script
        "auto-fitter"

        * include/freetype/ftoutln.h, src/base/ftoutln.c: adding the
        definition of FT_Outline_Get_Orientation, used to compute the
        fill orientation of a given glyph outline.

        * include/freetype/internal/ftserv.h: fixed trivial bug which
        could crashed the font engine when a cached service pointer was
        retrieved with FT_FACE_LOOKUP_SERVICE

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2003-10-01  David Turner  <[email protected]>
+
+        * src/autofit/*: adding first sources of the new multi-script
+        "auto-fitter"
+
+        * include/freetype/ftoutln.h, src/base/ftoutln.c: adding the
+        definition of FT_Outline_Get_Orientation, used to compute the
+        fill orientation of a given glyph outline.
+
+        * include/freetype/internal/ftserv.h: fixed trivial bug which
+        could crashed the font engine when a cached service pointer was
+        retrieved with FT_FACE_LOOKUP_SERVICE
+
 2003-09-30  Werner Lemberg  <[email protected]>
 
 	* src/cid/cidload.c (cid_parse_dict): Skip token if no keyword is
@@ -21,9 +34,9 @@
 2003-09-29  David Turner  <[email protected]>
 
 	Added new service to handle glyph name dictionaries, replacing the
-	old internal header named `psnames.h' by `services/svpsname.h'. 
+	old internal header named `psnames.h' by `services/svpsname.h'.
 	Note that this is different from `services/svpostnm.h' which only
-	handles the retrieval of PostScript font names for a given face. 
+	handles the retrieval of PostScript font names for a given face.
 	(Should we merge these two services into a single header?)
 
 	* include/freetype/internal/psnames.h: Removed.  Most of its
--- a/include/freetype/ftoutln.h
+++ b/include/freetype/ftoutln.h
@@ -389,6 +389,69 @@
                      FT_Raster_Params*  params );
 
 
+ /**************************************************************************
+  *
+  * @enum: FT_Orientation
+  *  
+  * @description:
+  *   a list of values used to describe an outline's contour orientation
+  *
+  *   The TrueType and Postscript specifications used different conventions
+  *   to determine wether outline contours should be filled or unfilled.
+  *   
+  * @values:
+  *   FT_ORIENTATION_TRUETYPE ::
+  *     according to the TrueType specification, clockwise contours must
+  *     be filled, and counter-clockwise ones must be unfilled
+  *
+  *   FT_ORIENTATION_POSTSCRIPT ::
+  *     according to the Postscript specification, counter-clockwise contours
+  *     must be filled, and clockwise ones must be unfilled
+  *
+  *   FT_ORIENTATION_FILL_RIGHT ::
+  *     this is identical to @FT_ORIENTATION_TRUETYPE, but is used to
+  *     remember that in TrueType, everything that is to the right of
+  *     the drawing direction of a contour must be filled.
+  *
+  *   FT_ORIENTATION_FILL_LEFT ::
+  *     this is identical to @FT_ORIENTATION_POSTSCRIPT, but is used to
+  *     remember that in Postscript, everything that is to the left of
+  *     the drawing direction of a contour must be filled
+  */
+  typedef enum
+  {
+    FT_ORIENTATION_TRUETYPE   = 0,
+    FT_ORIENTATION_POSTSCRIPT = 1,
+    FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE,
+    FT_ORIENTATION_FILL_LEFT  = FT_ORIENTATION_POSTSCRIPT
+  
+  } FT_Orientation;
+
+
+ /**************************************************************************
+  *
+  * @function: FT_Outline_Get_Orientation
+  *  
+  * @description:
+  *   this function analyzes a glyph outline and tries to compute its
+  *   fill orientation (see @FT_Orientation). This is done by computing
+  *   the direction of each global horizontal and/or vertical extrema
+  *   within the outline.
+  *
+  *   note that this will return @FT_ORIENTATION_TRUETYPE for empty
+  *   outlines.
+  *
+  * @input:
+  *   outline :: handle to source outline
+  *
+  * @return:
+  *   orientation
+  *
+  */
+  FT_EXPORT( FT_Orientation )
+  FT_Outline_Get_Orientation( FT_Outline*  outline );
+
+
   /* */
 
 
--- a/include/freetype/internal/ftserv.h
+++ b/include/freetype/internal/ftserv.h
@@ -216,8 +216,8 @@
       FT_FACE(face)->internal->services. service_ ## id =        \
         (FT_Pointer)( svc != NULL ? svc                          \
                                   : FT_SERVICE_UNAVAILABLE );    \
-      *pptr = svc;                                               \
     }                                                            \
+    *pptr = svc;                                                 \
   FT_END_STMNT
 
 
--- /dev/null
+++ b/src/autofit/afangles.c
@@ -1,0 +1,164 @@
+#include "aftypes.h"
+
+  /* this table was generated for AF_ANGLE_PI = 256 */
+#define AF_ANGLE_MAX_ITERS  8
+
+  static const FT_Fixed
+  af_angle_arctan_table[9] =
+  {
+    90, 64, 38, 20, 10, 5, 3, 1, 1 
+  };
+
+  static FT_Int
+  af_angle_prenorm( FT_Vector*  vec )
+  {
+    FT_Fixed  x, y, z;
+    FT_Int    shift;
+
+
+    x = vec->x;
+    y = vec->y;
+
+    z     = ( ( x >= 0 ) ? x : - x ) | ( (y >= 0) ? y : -y );
+    shift = 0;
+
+    if ( z < ( 1L << 27 ) )
+    {
+      do
+      {
+        shift++;
+        z <<= 1;
+      } while ( z < ( 1L << 27 ) );
+
+      vec->x = x << shift;
+      vec->y = y << shift;
+    }
+    else if ( z > ( 1L << 28 ) )
+    {
+      do
+      {
+        shift++;
+        z >>= 1;
+      } while ( z > ( 1L << 28 ) );
+
+      vec->x = x >> shift;
+      vec->y = y >> shift;
+      shift  = -shift;
+    }
+    return shift;
+  }
+
+
+  static void
+  af_angle_pseudo_polarize( FT_Vector*  vec )
+  {
+    FT_Fixed         theta;
+    FT_Fixed         yi, i;
+    FT_Fixed         x, y;
+    const FT_Fixed  *arctanptr;
+
+
+    x = vec->x;
+    y = vec->y;
+
+    /* Get the vector into the right half plane */
+    theta = 0;
+    if ( x < 0 )
+    {
+      x = -x;
+      y = -y;
+      theta = 2 * AF_ANGLE_PI2;
+    }
+
+    if ( y > 0 )
+      theta = - theta;
+
+    arctanptr = af_angle_arctan_table;
+
+    if ( y < 0 )
+    {
+      /* Rotate positive */
+      yi     = y + ( x << 1 );
+      x      = x - ( y << 1 );
+      y      = yi;
+      theta -= *arctanptr++;  /* Subtract angle */
+    }
+    else
+    {
+      /* Rotate negative */
+      yi     = y - ( x << 1 );
+      x      = x + ( y << 1 );
+      y      = yi;
+      theta += *arctanptr++;  /* Add angle */
+    }
+
+    i = 0;
+    do
+    {
+      if ( y < 0 )
+      {
+        /* Rotate positive */
+        yi     = y + ( x >> i );
+        x      = x - ( y >> i );
+        y      = yi;
+        theta -= *arctanptr++;
+      }
+      else
+      {
+        /* Rotate negative */
+        yi     = y - ( x >> i );
+        x      = x + ( y >> i );
+        y      = yi;
+        theta += *arctanptr++;
+      }
+    } while ( ++i < AF_TRIG_MAX_ITERS );
+
+    /* round theta */
+    if ( theta >= 0 )
+      theta = ( theta + 2 ) & -4;
+    else
+      theta = - (( -theta + 2 ) & -4);
+
+    vec->x = x;
+    vec->y = theta;
+  }
+
+
+  /* documentation is in fttrigon.h */
+
+  FT_LOCAL_DEF( AF_Angle )
+  af_angle_atan( FT_Fixed  dx,
+                 FT_Fixed  dy )
+  {
+    FT_Vector  v;
+
+
+    if ( dx == 0 && dy == 0 )
+      return 0;
+
+    v.x = dx;
+    v.y = dy;
+    af_angle_prenorm( &v );
+    af_angle_pseudo_polarize( &v );
+
+    return v.y;
+  }
+
+
+
+  FT_LOCAL_DEF( AF_Angle )
+  af_angle_diff( AF_Angle  angle1,
+                 AF_Angle  angle2 )
+  {
+    AF_Angle  delta = angle2 - angle1;
+    
+    delta %= AF_ANGLE_2PI;
+    if ( delta < 0 )
+      delta += AF_ANGLE_2PI;
+    
+    if ( delta > AF_ANGLE_PI )
+      delta -= AF_ANGLE_2PI;
+      
+    return delta;
+  }                 
+
--- /dev/null
+++ b/src/autofit/afhints.c
@@ -1,0 +1,626 @@
+#include "afhints.h"
+
+#ifdef AF_DEBUG
+
+#include <stdio.h>
+
+  void
+  af_outline_hints_dump_edges( AF_OutlineHints  hints )
+  {
+    AF_Edge     edges;
+    AF_Edge     edge_limit;
+    AF_Segment  segments;
+    FT_Int      dimension;
+
+
+    edges      = hints->horz_edges;
+    edge_limit = edges + hints->num_hedges;
+    segments   = hints->horz_segments;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AF_Edge   edge;
+
+
+      printf ( "Table of %s edges:\n",
+               !dimension ? "vertical" : "horizontal" );
+      printf ( "  [ index |  pos |  dir  | link |"
+               " serif | blue | opos  |  pos  ]\n" );
+
+      for ( edge = edges; edge < edge_limit; edge++ )
+      {
+        printf ( "  [ %5d | %4d | %5s | %4d | %5d |  %c  | %5.2f | %5.2f ]\n",
+                 edge - edges,
+                 (int)edge->fpos,
+                 edge->dir == AF_DIR_UP
+                   ? "up"
+                   : ( edge->dir == AF_DIR_DOWN
+                         ? "down"
+                         : ( edge->dir == AF_DIR_LEFT
+                               ? "left"
+                               : ( edge->dir == AF_DIR_RIGHT
+                                     ? "right"
+                                     : "none" ) ) ),
+                 edge->link ? ( edge->link - edges ) : -1,
+                 edge->serif ? ( edge->serif - edges ) : -1,
+                 edge->blue_edge ? 'y' : 'n',
+                 edge->opos / 64.0,
+                 edge->pos / 64.0 );
+      }
+
+      edges      = hints->vert_edges;
+      edge_limit = edges + hints->num_vedges;
+      segments   = hints->vert_segments;
+    }
+  }
+
+
+  /* A function used to dump the array of linked segments */
+  void
+  af_outline_hints_dump_segments( AF_OutlineHints  hints )
+  {
+    AF_Segment  segments;
+    AF_Segment  segment_limit;
+    AF_Point    points;
+    FT_Int      dimension;
+
+
+    points        = hints->points;
+    segments      = hints->horz_segments;
+    segment_limit = segments + hints->num_hsegments;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AF_Segment  seg;
+
+
+      printf ( "Table of %s segments:\n",
+               !dimension ? "vertical" : "horizontal" );
+      printf ( "  [ index |  pos |  dir  | link | serif |"
+               " numl | first | start ]\n" );
+
+      for ( seg = segments; seg < segment_limit; seg++ )
+      {
+        printf ( "  [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
+                 seg - segments,
+                 (int)seg->pos,
+                 seg->dir == AF_DIR_UP
+                   ? "up"
+                   : ( seg->dir == AF_DIR_DOWN
+                         ? "down"
+                         : ( seg->dir == AF_DIR_LEFT
+                               ? "left"
+                               : ( seg->dir == AF_DIR_RIGHT
+                                     ? "right"
+                                     : "none" ) ) ),
+                 seg->link ? ( seg->link - segments ) : -1,
+                 seg->serif ? ( seg->serif - segments ) : -1,
+                 (int)seg->num_linked,
+                 seg->first - points,
+                 seg->last - points );
+      }
+
+      segments      = hints->vert_segments;
+      segment_limit = segments + hints->num_vsegments;
+    }
+  }
+
+#endif /* AF_DEBUG */
+
+
+  /* compute the direction value of a given vector */
+  FT_LOCAL_DEF( AF_Direction )
+  af_direction_compute( FT_Pos  dx,
+                        FT_Pos  dy )
+  {
+    AF_Direction  dir;
+    FT_Pos        ax = ABS( dx );
+    FT_Pos        ay = ABS( dy );
+
+
+    dir = AF_DIR_NONE;
+
+    /* atan(1/12) == 4.7 degrees */
+
+    /* test for vertical direction */
+    if ( ax * 12 < ay )
+    {
+      dir = dy > 0 ? AF_DIR_UP : AF_DIR_DOWN;
+    }
+    /* test for horizontal direction */
+    else if ( ay * 12 < ax )
+    {
+      dir = dx > 0 ? AF_DIR_RIGHT : AF_DIR_LEFT;
+    }
+
+    return dir;
+  }
+
+
+  /* compute all inflex points in a given glyph */
+  static void
+  af_outline_hints_compute_inflections( AF_OutlineHints  hints )
+  {
+    AF_Point*  contour       = hints->contours;
+    AF_Point*  contour_limit = contour + hints->num_contours;
+
+
+    /* load original coordinates in (u,v) */
+    af_outline_hints_setup_uv( hints, outline, AF_UV_FXY );
+
+    /* do each contour separately */
+    for ( ; contour < contour_limit; contour++ )
+    {
+      AF_Point   point = contour[0];
+      AF_Point   first = point;
+      AF_Point   start = point;
+      AF_Point   end   = point;
+      AF_Point   before;
+      AF_Point   after;
+      AF_Angle   angle_in, angle_seg, angle_out;
+      AF_Angle   diff_in, diff_out;
+      FT_Int     finished = 0;
+
+
+      /* compute first segment in contour */
+      first = point;
+
+      start = end = first;
+      do
+      {
+        end = end->next;
+        if ( end == first )
+          goto Skip;
+
+      } while ( end->u == first->u && end->v == first->v );
+
+      angle_seg = af_angle( end->u - start->u, 
+                            end->v - start->v );
+
+      /* extend the segment start whenever possible */
+      before = start;
+      do
+      {
+        do
+        {
+          start  = before;
+          before = before->prev;
+          if ( before == first )
+            goto Skip;
+
+        } while ( before->u == start->u && before->v == start->v );
+
+        angle_in = af_angle( start->u - before->u, 
+                             start->v - before->v );
+
+      } while ( angle_in == angle_seg );
+
+      first   = start;
+      diff_in = af_angle_diff( angle_in, angle_seg );
+
+      /* now, process all segments in the contour */
+      do
+      {
+        /* first, extend current segment's end whenever possible */
+        after = end;
+        do
+        {
+          do
+          {
+            end   = after;
+            after = after->next;
+            if ( after == first )
+              finished = 1;
+
+          } while ( end->u == after->u && end->v == after->v );
+
+          vec.x     = after->u - end->u;
+          vec.y     = after->v - end->v;
+          angle_out = af_angle( after->u - end->u,
+                                after->v - end->v );
+
+        } while ( angle_out == angle_seg );
+
+        diff_out = af_angle_diff( angle_seg, angle_out );
+
+        if ( ( diff_in ^ diff_out ) < 0 )
+        {
+          /* diff_in and diff_out have different signs, we have */
+          /* inflection points here...                          */
+          do
+          {
+            start->flags |= AF_FLAG_INFLECTION;
+            start = start->next;
+
+          } while ( start != end );
+
+          start->flags |= AF_FLAG_INFLECTION;
+        }
+
+        start     = end;
+        end       = after;
+        angle_seg = angle_out;
+        diff_in   = diff_out;
+
+      } while ( !finished );
+
+    Skip:
+      ;
+    }
+  }
+
+
+
+  FT_LOCAL_DEF( void )
+  af_outline_hints_init( AF_OutlineHints  hints,
+                         FT_Memory        memory )
+  {
+    FT_ZERO( hints );
+    hints->memory = memory;
+  }                         
+
+
+
+  FT_LOCAL_DEF( void )
+  af_outline_hints_done( AF_OutlineHints  hints )
+  {
+    if ( hints && hints->memory )
+    {
+      FT_Memory     memory = hints->memory;
+      AF_Dimension  dim;
+
+     /* note that we don't need to free the segment and edge
+      * buffers, since they're really within the hints->points array
+      */
+      for ( dim = 0; dim < 2; dim++ )
+      {
+        AF_AxisHints  axis = &hints->axis[ dim ];
+        
+        axis->num_segments = 0;
+        axis->num_edges    = 0;
+        axis->segments     = NULL;
+        axis->edges        = NULL;
+      }
+
+      FT_FREE( hints->contours );
+      hints->max_contours = 0;
+      hints->num_contours = 0;
+      
+      FT_FREE( hints->points );
+      hints->num_points = 0;
+      hints->max_points = 0;
+      
+      hints->memory = NULL;
+    }
+  }
+
+
+
+  FT_LOCAL_DEF( FT_Error )
+  af_outline_hints_reset( AF_OutlineHints  hints,
+                          FT_Outline*      outline,
+                          FT_Fixed         x_scale,
+                          FT_Fixed         y_scale )
+  {
+    FT_Error     error        = AF_Err_Ok;
+    
+    FT_UInt      old_max, new_max;
+
+    hints->num_points    = 0;
+    hints->num_contours  = 0;
+    
+    hints->axis[0].num_segments = 0;
+    hints->axis[0].num_edges    = 0;
+    hints->axis[1].num_segments = 0;
+    hints->axis[1].num_edges    = 0;
+    
+   /* first of all, reallocate the contours array when necessary
+    */
+    new_max = (FT_UInt) outline->n_contours;
+    old_max = hints->max_contours;
+    if ( new_max > old_max )
+    {
+      new_max = (new_max + 3) & ~3;
+      
+      if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
+        goto Exit;
+      
+      hints->max_contours = new_max;
+    }
+
+   /* then, reallocate the points, segments & edges arrays if needed --   
+    * note that we reserved two additional point positions, used to       
+    * hint metrics appropriately                                          
+    */                                                                    
+    new_max = (FT_UInt)( outline->n_points + 2 );
+    old_max = hints->max_points;
+    if ( new_max > old_max )
+    {
+      FT_Byte*    items;
+      FT_ULong    off1, off2, off3;
+      
+     /* we store in a single buffer the following arrays:
+      *
+      *  - an array of   N  AF_PointRec   items
+      *  - an array of 2*N  AF_SegmentRec items
+      *  - an array of 2*N  AF_EdgeRec    items 
+      *
+      */
+      
+      new_max = ( new_max + 2 + 7 ) & ~7;
+      
+#undef  OFF_INCREMENT
+#define OFF_INCREMENT( _off, _type, _count )   \
+     ((((_off) + sizeof(_type)) & ~(sizeof(_type)) + ((_count)*sizeof(_type)))
+
+      off1 = OFF_INCREMENT( 0, AF_PointRec, new_max );
+      off2 = OFF_INCREMENT( off1, AF_SegmentRec, new_max );
+      off3 = OFF_INCREMENT( off2, AF_EdgeRec, new_max*2 );
+
+      FT_FREE( hints->points );
+
+      if ( FT_ALLOC( items, off3 ) )
+      {
+        hints->max_points       = 0;
+        hints->axis[0].segments = NULL;
+        hints->axis[0].edges    = NULL;
+        hints->axis[1].segments = NULL;
+        hints->axis[1].edges    = NULL;
+        goto Exit;
+      }
+      
+     /* readjust some pointers
+      */
+      hints->max_points       = new_max;
+      hints->points           = (AF_Point) items;
+      
+      hints->axis[0].segments = (AF_Segment)( items + off1 );
+      hints->axis[1].segments = hints->axis[0].segments + new_max;
+      
+      hints->axis[0].edges    = (AF_Edge)   ( items + off2 );
+      hints->axis[1].edges    = hints->axis[0].edges + new_max;
+    }
+
+    hints->num_points   = outline->n_points;
+    hints->num_contours = outline->n_contours;
+
+
+    /* We can't rely on the value of `FT_Outline.flags' to know the fill  */
+    /* direction used for a glyph, given that some fonts are broken (e.g. */
+    /* the Arphic ones).  We thus recompute it each time we need to.      */
+    /*                                                                    */
+    hints->axis[ AF_DIMENSION_HORZ ].major_dir = AF_DIR_UP;
+    hints->axis[ AF_DIMENSION_VERT ].major_dir = AF_DIR_LEFT;
+
+    if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
+    {
+      hints->axis[ AF_DIMENSION_HORZ ].major_dir = AF_DIR_DOWN;
+      hints->axis[ AF_DIMENSION_VERT ].major_dir = AF_DIR_RIGHT;
+    }
+
+    hints->x_scale = x_scale;
+    hints->y_scale = y_scale;
+
+    points = hints->points;
+    if ( hints->num_points == 0 )
+      goto Exit;
+
+    {
+      /* do one thing at a time -- it is easier to understand, and */
+      /* the code is clearer                                       */
+      AF_Point  point;
+      AF_Point  point_limit = points + hints->num_points;
+
+
+      /* compute coordinates & bezier flags */
+      {
+        FT_Vector*  vec = outline->points;
+        char*       tag = outline->tags;
+
+
+        for ( point = points; point < point_limit; point++, vec++, tag++ )
+        {
+          point->fx = vec->x;
+          point->fy = vec->y;
+          point->ox = point->x = FT_MulFix( vec->x, x_scale );
+          point->oy = point->y = FT_MulFix( vec->y, y_scale );
+
+          switch ( FT_CURVE_TAG( *tag ) )
+          {
+          case FT_CURVE_TAG_CONIC:
+            point->flags = AF_FLAG_CONIC;
+            break;
+          case FT_CURVE_TAG_CUBIC:
+            point->flags = AF_FLAG_CUBIC;
+            break;
+          default:
+            point->flags = 0;
+            ;
+          }
+        }
+      }
+
+      /* compute `next' and `prev' */
+      {
+        FT_Int    contour_index;
+        AF_Point  prev;
+        AF_Point  first;
+        AF_Point  end;
+
+
+        contour_index = 0;
+
+        first = points;
+        end   = points + outline->contours[0];
+        prev  = end;
+
+        for ( point = points; point < point_limit; point++ )
+        {
+          point->prev = prev;
+          if ( point < end )
+          {
+            point->next = point + 1;
+            prev        = point;
+          }
+          else
+          {
+            point->next = first;
+            contour_index++;
+            if ( point + 1 < point_limit )
+            {
+              end   = points + source->contours[contour_index];
+              first = point + 1;
+              prev  = end;
+            }
+          }
+        }
+      }
+
+      /* set-up the contours array */
+      {
+        AF_Point*  contour       = hints->contours;
+        AF_Point*  contour_limit = contour + hints->num_contours;
+        short*     end           = outline->contours;
+        short      idx           = 0;
+
+
+        for ( ; contour < contour_limit; contour++, end++ )
+        {
+          contour[0] = points + idx;
+          idx        = (short)( end[0] + 1 );
+        }
+      }
+
+      /* compute directions of in & out vectors */
+      {
+        for ( point = points; point < point_limit; point++ )
+        {
+          AF_Point   prev;
+          AF_Point   next;
+          FT_Pos     in_x, in_y, out_x, out_y;
+
+
+          prev   = point->prev;
+          in_x   = point->fx - prev->fx;
+          in_y   = point->fy - prev->fy;
+          
+          point->in_dir = af_compute_direction( in_x, in_y );
+
+          next   = point->next;
+          out_x  = next->fx - point->fx;
+          out_y  = next->fy - point->fy;
+
+          point->out_dir = af_compute_direction( out_x, out_y );
+
+          if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) )
+          {
+          Is_Weak_Point:
+            point->flags |= AF_FLAG_WEAK_INTERPOLATION;
+          }
+          else if ( point->out_dir == point->in_dir )
+          {
+            AF_Angle  angle_in, angle_out, delta;
+
+
+            if ( point->out_dir != AF_DIR_NONE )
+              goto Is_Weak_Point;
+
+            angle_in  = af_angle( in_x, in_y );
+            angle_out = af_angle( out_x, out_y );
+            delta     = af_angle_diff( angle_in, angle_out );
+
+            if ( delta < 2 && delta > -2 )
+              goto Is_Weak_Point;
+          }
+          else if ( point->in_dir == -point->out_dir )
+            goto Is_Weak_Point;
+        }
+      }
+    }
+
+   /* compute inflection points
+    */    
+    af_outline_hints_compute_inflections( hints );
+
+  Exit:
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  af_outline_hints_setup_uv( AF_OutlineHints  hints,
+                             AF_UV            source )
+  {
+    AF_Point  point       = hints->points;
+    AF_Point  point_limit = point + hints->num_points;
+
+
+    switch ( source )
+    {
+    case AF_UV_FXY:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->fx;
+        point->v = point->fy;
+      }
+      break;
+
+    case AF_UV_FYX:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->fy;
+        point->v = point->fx;
+      }
+      break;
+
+    case AF_UV_OXY:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->ox;
+        point->v = point->oy;
+      }
+      break;
+
+    case AF_UV_OYX:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->oy;
+        point->v = point->ox;
+      }
+      break;
+
+    case AF_UV_YX:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->y;
+        point->v = point->x;
+      }
+      break;
+
+    case AF_UV_OX:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->x;
+        point->v = point->ox;
+      }
+      break;
+
+    case AF_UV_OY:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->y;
+        point->v = point->oy;
+      }
+      break;
+
+    default:
+      for ( ; point < point_limit; point++ )
+      {
+        point->u = point->x;
+        point->v = point->y;
+      }
+    }
+  }
+
+
+
--- /dev/null
+++ b/src/autofit/afhints.h
@@ -1,0 +1,233 @@
+#ifndef __AFHINTS_H__
+#define __AFHINTS_H__
+
+#include "aftypes.h"
+
+FT_BEGIN_HEADER
+
+ /*
+  *  The definition of outline hints. These are shared by all
+  *  script analysis routines
+  *
+  */
+
+  typedef enum
+  {
+    AF_DIMENSION_HORZ = 0,  /* x coordinates, i.e. vertical segments & edges   */
+    AF_DIMENSION_VERT = 1,  /* y coordinates, i.e. horizontal segments & edges */
+
+    AF_DIMENSION_MAX  /* do not remove */
+  
+  } AF_Dimension;
+
+
+  /* hint directions -- the values are computed so that two vectors are */
+  /* in opposite directions iff `dir1+dir2 == 0'                        */
+  typedef enum
+  {
+    AF_DIR_NONE  =  4,
+    AF_DIR_RIGHT =  1,
+    AF_DIR_LEFT  = -1,
+    AF_DIR_UP    =  2,
+    AF_DIR_DOWN  = -2
+    
+  } AF_Direction;
+
+
+  /* point hint flags */
+  typedef enum
+  {
+    AF_FLAG_NONE    = 0,
+    
+   /* point type flags */
+    AF_FLAG_CONIC   = (1 << 0),
+    AF_FLAG_CUBIC   = (1 << 1),
+    AF_FLAG_CONTROL = AF_FLAG_CONIC | AF_FLAG_CUBIC,
+    
+   /* point extremum flags */
+    AF_FLAG_EXTREMA_X = (1 << 2),
+    AF_FLAG_EXTREMA_Y = (1 << 3),
+    
+   /* point roundness flags */
+    AF_FLAG_ROUND_X = (1 << 4),
+    AF_FLAG_ROUND_Y = (1 << 5),
+    
+   /* point touch flags */
+    AF_FLAG_TOUCH_X = (1 << 6),
+    AF_FLAG_TOUCH_Y = (1 << 7),
+    
+   /* candidates for weak interpolation have this flag set */
+    AF_FLAG_WEAK_INTERPOLATION = (1 << 8),
+    
+   /* all inflection points in the outline have this flag set */
+    AF_FLAG_INFLECTION         = (1 << 9)
+  
+  } AF_Flags;
+  
+
+  /* edge hint flags */
+  typedef enum
+  {
+    AF_EDGE_NORMAL = 0,
+    AF_EDGE_ROUND  = (1 << 0),
+    AF_EDGE_SERIF  = (1 << 1),
+    AF_EDGE_DONE   = (1 << 2)
+  
+  } AF_Edge_Flags;
+
+
+
+  typedef struct AF_PointRec_*    AF_Point;
+  typedef struct AF_SegmentRec_*  AF_Segment;
+  typedef struct AF_EdgeRec_*     AF_Edge;
+
+
+  typedef struct  AF_PointRec_
+  {
+    AF_Flags      flags;    /* point flags used by hinter */
+    FT_Pos        ox, oy;   /* original, scaled position  */
+    FT_Pos        fx, fy;   /* original, unscaled position (font units) */
+    FT_Pos        x,  y;    /* current position */
+    FT_Pos        u,  v;    /* current (x,y) or (y,x) depending on context */
+
+    AF_Direction  in_dir;   /* direction of inwards vector  */
+    AF_Direction  out_dir;  /* direction of outwards vector */
+
+    AF_Point      next;     /* next point in contour     */
+    AF_Point      prev;     /* previous point in contour */
+
+  } AF_PointRec;
+
+
+  typedef struct  AF_SegmentRec_
+  {
+    AF_Edge_Flags  flags;       /* edge/segment flags for this segment */
+    AF_Direction   dir;         /* segment direction                   */
+    FT_Pos         pos;         /* position of segment                 */
+    FT_Pos         min_coord;   /* minimum coordinate of segment       */
+    FT_Pos         max_coord;   /* maximum coordinate of segment       */
+
+    AF_Edge        edge;        /* the segment's parent edge */
+    AF_Segment     edge_next;   /* link to next segment in parent edge */
+
+    AF_Segment     link;        /* link segment               */
+    AF_Segment     serif;       /* primary segment for serifs */
+    FT_Pos         num_linked;  /* number of linked segments  */
+    FT_Pos         score;
+
+    AF_Point       first;       /* first point in edge segment             */
+    AF_Point       last;        /* last point in edge segment              */
+    AF_Point*      contour;     /* ptr to first point of segment's contour */
+
+  } AF_SegmentRec;
+
+
+  typedef struct  AF_EdgeRec_
+  {
+    FT_Pos         fpos;       /* original, unscaled position (font units) */
+    FT_Pos         opos;       /* original, scaled position                */
+    FT_Pos         pos;        /* current position                         */
+
+    AF_Edge_Flags  flags;      /* edge flags */
+    AF_Direction   dir;        /* edge direction */
+    FT_Fixed       scale;      /* used to speed up interpolation between edges */
+    FT_Pos*        blue_edge;  /* non-NULL if this is a blue edge              */
+
+    AF_Edge        link;
+    AF_Edge        serif;
+    FT_Int         num_linked;
+
+    FT_Int         score;
+
+    AF_Segment     first;
+    AF_Segment     last;
+
+
+  } AF_EdgeRec;
+
+
+  typedef struct AF_AxisHintsRec_
+  {
+    FT_Int        num_segments;
+    AF_Segment    segments;
+  
+    FT_Int        num_edges;
+    AF_Edge       edges;
+
+    AF_Direction  major_dir;
+
+  } AF_AxisHintsRec, *AF_AxisHints;
+
+  
+  typedef struct AF_OutlineHintsRec_
+  {
+    FT_Memory     memory;
+
+    FT_Fixed      x_scale;
+    FT_Fixed      y_scale;
+    FT_Pos        edge_distance_threshold;
+
+    FT_Int        max_points;
+    FT_Int        num_points;
+    AF_Point      points;
+
+    FT_Int        max_contours;
+    FT_Int        num_contours;
+    AF_Point*     contours;
+
+    AF_AxisHintsRec  axis[ AF_DIMENSION_MAX ];
+
+  } AF_OutlineHintsRec;
+
+
+
+
+  FT_LOCAL( AF_Direction )
+  af_direction_compute( FT_Pos  dx,
+                        FT_Pos  dy );
+
+
+  FT_LOCAL( void )
+  af_outline_hints_init( AF_OutlineHints  hints );
+
+
+ /*  used to set the (u,v) fields of each AF_Point in a AF_OutlineHints
+  *  object.
+  */
+  typedef enum  AH_UV_
+  {
+    AH_UV_FXY,  /* (u,v) = (fx,fy) */
+    AH_UV_FYX,  /* (u,v) = (fy,fx) */
+    AH_UV_OXY,  /* (u,v) = (ox,oy) */
+    AH_UV_OYX,  /* (u,v) = (oy,ox) */
+    AH_UV_OX,   /* (u,v) = (ox,x)  */
+    AH_UV_OY,   /* (u,v) = (oy,y)  */
+    AH_UV_YX,   /* (u,v) = (y,x)   */
+    AH_UV_XY    /* (u,v) = (x,y)   *   should always be last! */
+
+  } AH_UV;
+
+  FT_LOCAL_DEF( void )
+  af_outline_hints_setup_uv( AF_OutlineHints  hints,
+                             AF_UV            source );
+
+
+ /*  recomputes all AF_Point in a AF_OutlineHints from the definitions
+  *  in a source outline
+  */
+  FT_LOCAL( FT_Error )
+  af_outline_hints_reset( AF_OutlineHints  hints,
+                          FT_Outline*      outline,
+                          FT_Fixed         x_scale,
+                          FT_Fixed         y_scale );
+
+  FT_LOCAL( void )
+  af_outline_hints_done( AF_OutlineHints  hints );
+
+
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFHINTS_H__ */
--- /dev/null
+++ b/src/autofit/aflatin.c
@@ -1,0 +1,755 @@
+#include "aflatin.h"
+
+  FT_LOCAL_DEF( void )
+  af_latin_hints_compute_segments( AF_OutlineHints  hints,
+                                   AF_Dimension     dim )
+  {
+    AF_AxisHints  axis = &hints->axis[dim];
+    AF_Segment    segments = axis->segments;
+    AF_Segment    segment       =  segments;
+    FT_Int        num_segments  =  0;
+    AF_Point*     contour       =  hints->contours;
+    AF_Point*     contour_limit =  contour + hints->num_contours;
+    AF_Direction  major_dir;
+
+#ifdef AF_HINT_METRICS
+    AF_Point    min_point     =  0;
+    AF_Point    max_point     =  0;
+    FT_Pos      min_coord     =  32000;
+    FT_Pos      max_coord     = -32000;
+#endif
+
+    major_dir   = ABS( axis->major_dir );
+    segment_dir = major_dir;
+
+    /* set up (u,v) in each point */
+    af_setup_uv( outline, (dim == AF_DIMENSION_HORZ)
+                        ? AF_UV_FXY,
+                        : AF_UV_FYX );
+
+
+    /* do each contour separately */
+    for ( ; contour < contour_limit; contour++ )
+    {
+      AF_Point  point   =  contour[0];
+      AF_Point  last    =  point->prev;
+      int       on_edge =  0;
+      FT_Pos    min_pos =  32000;  /* minimum segment pos != min_coord */
+      FT_Pos    max_pos = -32000;  /* maximum segment pos != max_coord */
+      FT_Bool   passed;
+
+
+#ifdef AF_HINT_METRICS
+      if ( point->u < min_coord )
+      {
+        min_coord = point->u;
+        min_point = point;
+      }
+      if ( point->u > max_coord )
+      {
+        max_coord = point->u;
+        max_point = point;
+      }
+#endif
+
+      if ( point == last )  /* skip singletons -- just in case */
+        continue;
+
+      if ( ABS( last->out_dir )  == major_dir &&
+           ABS( point->out_dir ) == major_dir )
+      {
+        /* we are already on an edge, try to locate its start */
+        last = point;
+
+        for (;;)
+        {
+          point = point->prev;
+          if ( ABS( point->out_dir ) != major_dir )
+          {
+            point = point->next;
+            break;
+          }
+          if ( point == last )
+            break;
+        }
+      }
+
+      last   = point;
+      passed = 0;
+
+      for (;;)
+      {
+        FT_Pos  u, v;
+
+
+        if ( on_edge )
+        {
+          u = point->u;
+          if ( u < min_pos )
+            min_pos = u;
+          if ( u > max_pos )
+            max_pos = u;
+
+          if ( point->out_dir != segment_dir || point == last )
+          {
+            /* we are just leaving an edge; record a new segment! */
+            segment->last = point;
+            segment->pos  = ( min_pos + max_pos ) >> 1;
+
+            /* a segment is round if either its first or last point */
+            /* is a control point                                   */
+            if ( ( segment->first->flags | point->flags ) &
+                   AF_FLAG_CONTROL                        )
+              segment->flags |= AF_EDGE_ROUND;
+
+            /* compute segment size */
+            min_pos = max_pos = point->v;
+
+            v = segment->first->v;
+            if ( v < min_pos )
+              min_pos = v;
+            if ( v > max_pos )
+              max_pos = v;
+
+            segment->min_coord = min_pos;
+            segment->max_coord = max_pos;
+
+            on_edge = 0;
+            num_segments++;
+            segment++;
+            /* fallthrough */
+          }
+        }
+
+        /* now exit if we are at the start/end point */
+        if ( point == last )
+        {
+          if ( passed )
+            break;
+          passed = 1;
+        }
+
+        if ( !on_edge && ABS( point->out_dir ) == major_dir )
+        {
+          /* this is the start of a new segment! */
+          segment_dir = point->out_dir;
+
+          /* clear all segment fields */
+          FT_ZERO( segment );
+
+          segment->dir      = segment_dir;
+          segment->flags    = AF_EDGE_NORMAL;
+          min_pos = max_pos = point->u;
+          segment->first    = point;
+          segment->last     = point;
+          segment->contour  = contour;
+          segment->score    = 32000;
+          segment->link     = NULL;
+          on_edge           = 1;
+
+#ifdef AF_HINT_METRICS
+          if ( point == max_point )
+            max_point = 0;
+
+          if ( point == min_point )
+            min_point = 0;
+#endif
+        }
+
+        point = point->next;
+      }
+
+    } /* contours */
+
+#ifdef AF_HINT_METRICS
+    /* we need to ensure that there are edges on the left-most and  */
+    /* right-most points of the glyph in order to hint the metrics; */
+    /* we do this by inserting fake segments when needed            */
+    if ( dim == AF_DIMENSION_HORZ )
+    {
+      AF_Point  point       = hints->points;
+      AF_Point  point_limit = point + hints->num_points;
+
+      FT_Pos    min_pos =  32000;
+      FT_Pos    max_pos = -32000;
+
+
+      min_point = 0;
+      max_point = 0;
+
+      /* compute minimum and maximum points */
+      for ( ; point < point_limit; point++ )
+      {
+        FT_Pos  x = point->fx;
+
+
+        if ( x < min_pos )
+        {
+          min_pos   = x;
+          min_point = point;
+        }
+        if ( x > max_pos )
+        {
+          max_pos   = x;
+          max_point = point;
+        }
+      }
+
+      /* insert minimum segment */
+      if ( min_point )
+      {
+        /* clear all segment fields */
+        FT_ZERO( segment );
+
+        segment->dir   = segment_dir;
+        segment->flags = AF_EDGE_NORMAL;
+        segment->first = min_point;
+        segment->last  = min_point;
+        segment->pos   = min_pos;
+        segment->score = 32000;
+        segment->link  = NULL;
+
+        num_segments++;
+        segment++;
+      }
+
+      /* insert maximum segment */
+      if ( max_point )
+      {
+        /* clear all segment fields */
+        FT_ZERO( segment );
+
+        segment->dir   = segment_dir;
+        segment->flags = AF_EDGE_NORMAL;
+        segment->first = max_point;
+        segment->last  = max_point;
+        segment->pos   = max_pos;
+        segment->score = 32000;
+        segment->link  = NULL;
+
+        num_segments++;
+        segment++;
+      }
+    }
+#endif /* AF_HINT_METRICS */
+
+    axis->num_segments = num_segments;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  af_latin_hints_link_segments( AF_OutlineHints  hints,
+                                AF_Dimension     dim )
+  {
+    AF_AxisHints  axis          = &hints->axis[dim];
+    AF_Segment    segments      = axis->segments;
+    AF_Segment    segment_limit = segments + axis->num_segments;
+    AF_Direction  major_dir     = axis->major_dir;
+    AF_Segment    seg1, seg2;
+
+    /* now compare each segment to the others */
+    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+    {
+      /* the fake segments are introduced to hint the metrics -- */
+      /* we must never link them to anything                     */
+      if ( seg1->first == seg1->last || seg1->dir != major_dir )
+        continue;
+
+      for ( seg2 = segments; seg2 < segment_limit; seg2++ )
+        if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
+        {
+          FT_Pos  pos1 = seg1->pos;
+          FT_Pos  pos2 = seg2->pos;
+          FT_Pos  dist = pos2 - pos1;
+
+
+          if ( dist < 0 )
+            continue;
+
+          {
+            FT_Pos  min = seg1->min_coord;
+            FT_Pos  max = seg1->max_coord;
+            FT_Pos  len, score;
+
+
+            if ( min < seg2->min_coord )
+              min = seg2->min_coord;
+
+            if ( max > seg2->max_coord )
+              max = seg2->max_coord;
+
+            len = max - min;
+            if ( len >= 8 )
+            {
+              score = dist + 3000 / len;
+
+              if ( score < seg1->score )
+              {
+                seg1->score = score;
+                seg1->link  = seg2;
+              }
+
+              if ( score < seg2->score )
+              {
+                seg2->score = score;
+                seg2->link  = seg1;
+              }
+            }
+          }
+        }
+    }
+
+    /* now, compute the `serif' segments */
+    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+    {
+      seg2 = seg1->link;
+
+      if ( seg2 )
+      {
+        seg2->num_linked++;
+        if ( seg2->link != seg1 )
+        {
+          seg1->link  = 0;
+          seg1->serif = seg2->link;
+        }
+      }
+    }
+  }
+
+
+  FT_LOCAL_DEF( void )
+  af_latin_hints_compute_edges( AF_OutlineHints  hints,
+                                AF_Dimension     dim )
+  {
+    AF_AxisHints  axis = &hints->axis[dim];
+    AF_Edge       edges = axis->edges;
+    AF_Edge       edge, edge_limit;
+    
+    AF_Segment    segments = axis->segments;
+    AF_Segment    segment_limit = segments + axis->num_segments;
+    AF_Segment    seg;
+    
+    AF_Direction  up_dir;
+    FT_Fixed      scale;
+    FT_Pos        edge_distance_threshold;
+
+
+    scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
+                                         : hints->y_scale;
+
+    up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
+                                          : AF_DIR_RIGHT;
+
+    /*********************************************************************/
+    /*                                                                   */
+    /* We will begin by generating a sorted table of edges for the       */
+    /* current direction.  To do so, we simply scan each segment and try */
+    /* to find an edge in our table that corresponds to its position.    */
+    /*                                                                   */
+    /* If no edge is found, we create and insert a new edge in the       */
+    /* sorted table.  Otherwise, we simply add the segment to the edge's */
+    /* list which will be processed in the second step to compute the    */
+    /* edge's properties.                                                */
+    /*                                                                   */
+    /* Note that the edges table is sorted along the segment/edge        */
+    /* position.                                                         */
+    /*                                                                   */
+    /*********************************************************************/
+
+    edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold,
+                                         scale );
+    if ( edge_distance_threshold > 64 / 4 )
+      edge_distance_threshold = 64 / 4;
+
+    edge_distance_threshold = FT_DivFix( edge_distance_threshold,
+                                         scale );
+
+    edge_limit = edges;
+    for ( seg = segments; seg < segment_limit; seg++ )
+    {
+      AF_Edge  found = 0;
+
+
+      /* look for an edge corresponding to the segment */
+      for ( edge = edges; edge < edge_limit; edge++ )
+      {
+        FT_Pos  dist;
+
+
+        dist = seg->pos - edge->fpos;
+        if ( dist < 0 )
+          dist = -dist;
+
+        if ( dist < edge_distance_threshold )
+        {
+          found = edge;
+          break;
+        }
+      }
+
+      if ( !found )
+      {
+        /* insert a new edge in the list and */
+        /* sort according to the position    */
+        while ( edge > edges && edge[-1].fpos > seg->pos )
+        {
+          edge[0] = edge[-1];
+          edge--;
+        }
+        edge_limit++;
+
+        /* clear all edge fields */
+        FT_MEM_ZERO( edge, sizeof ( *edge ) );
+
+        /* add the segment to the new edge's list */
+        edge->first    = seg;
+        edge->last     = seg;
+        edge->fpos     = seg->pos;
+        edge->opos     = edge->pos = FT_MulFix( seg->pos, scale );
+        seg->edge_next = seg;
+      }
+      else
+      {
+        /* if an edge was found, simply add the segment to the edge's */
+        /* list                                                       */
+        seg->edge_next        = edge->first;
+        edge->last->edge_next = seg;
+        edge->last            = seg;
+      }
+    }
+    *p_num_edges = (FT_Int)( edge_limit - edges );
+
+
+    /*********************************************************************/
+    /*                                                                   */
+    /* Good, we will now compute each edge's properties according to     */
+    /* segments found on its position.  Basically, these are:            */
+    /*                                                                   */
+    /*  - edge's main direction                                          */
+    /*  - stem edge, serif edge or both (which defaults to stem then)    */
+    /*  - rounded edge, straight or both (which defaults to straight)    */
+    /*  - link for edge                                                  */
+    /*                                                                   */
+    /*********************************************************************/
+
+    /* first of all, set the `edge' field in each segment -- this is */
+    /* required in order to compute edge links                       */
+
+    /* Note that I've tried to remove this loop, setting
+     * the "edge" field of each segment directly in the
+     * code above.  For some reason, it slows down execution
+     * speed -- on a Sun.
+     */
+    for ( edge = edges; edge < edge_limit; edge++ )
+    {
+      seg = edge->first;
+      if ( seg )
+        do
+        {
+          seg->edge = edge;
+          seg       = seg->edge_next;
+        }
+        while ( seg != edge->first );
+    }
+
+    /* now, compute each edge properties */
+    for ( edge = edges; edge < edge_limit; edge++ )
+    {
+      FT_Int  is_round    = 0;  /* does it contain round segments?    */
+      FT_Int  is_straight = 0;  /* does it contain straight segments? */
+      FT_Pos  ups         = 0;  /* number of upwards segments         */
+      FT_Pos  downs       = 0;  /* number of downwards segments       */
+
+
+      seg = edge->first;
+
+      do
+      {
+        FT_Bool  is_serif;
+
+
+        /* check for roundness of segment */
+        if ( seg->flags & AF_EDGE_ROUND )
+          is_round++;
+        else
+          is_straight++;
+
+        /* check for segment direction */
+        if ( seg->dir == up_dir )
+          ups   += seg->max_coord-seg->min_coord;
+        else
+          downs += seg->max_coord-seg->min_coord;
+
+        /* check for links -- if seg->serif is set, then seg->link must */
+        /* be ignored                                                   */
+        is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
+
+        if ( seg->link || is_serif )
+        {
+          AF_Edge     edge2;
+          AF_Segment  seg2;
+
+
+          edge2 = edge->link;
+          seg2  = seg->link;
+
+          if ( is_serif )
+          {
+            seg2  = seg->serif;
+            edge2 = edge->serif;
+          }
+
+          if ( edge2 )
+          {
+            FT_Pos  edge_delta;
+            FT_Pos  seg_delta;
+
+
+            edge_delta = edge->fpos - edge2->fpos;
+            if ( edge_delta < 0 )
+              edge_delta = -edge_delta;
+
+            seg_delta = seg->pos - seg2->pos;
+            if ( seg_delta < 0 )
+              seg_delta = -seg_delta;
+
+            if ( seg_delta < edge_delta )
+              edge2 = seg2->edge;
+          }
+          else
+            edge2 = seg2->edge;
+
+#ifdef FT_CONFIG_CHESTER_SERIF
+          if ( is_serif )
+          {
+            edge->serif   = edge2;
+            edge2->flags |= AF_EDGE_SERIF;
+          }
+          else
+            edge->link  = edge2;
+#else /* !FT_CONFIG_CHESTER_SERIF */
+          if ( is_serif )
+            edge->serif = edge2;
+          else
+            edge->link  = edge2;
+#endif /* !FT_CONFIG_CHESTER_SERIF */
+        }
+
+        seg = seg->edge_next;
+
+      } while ( seg != edge->first );
+
+      /* set the round/straight flags */
+      edge->flags = AF_EDGE_NORMAL;
+
+      if ( is_round > 0 && is_round >= is_straight )
+        edge->flags |= AF_EDGE_ROUND;
+
+      /* set the edge's main direction */
+      edge->dir = AF_DIR_NONE;
+
+      if ( ups > downs )
+        edge->dir = up_dir;
+
+      else if ( ups < downs )
+        edge->dir = -up_dir;
+
+      else if ( ups == downs )
+        edge->dir = 0;  /* both up and down! */
+
+      /* gets rid of serifs if link is set                */
+      /* XXX: This gets rid of many unpleasant artefacts! */
+      /*      Example: the `c' in cour.pfa at size 13     */
+
+      if ( edge->serif && edge->link )
+        edge->serif = 0;
+    }
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    af_outline_detect_features                                         */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Performs feature detection on a given AF_OutlineRec object.        */
+  /*                                                                       */
+  FT_LOCAL_DEF( void )
+  af_latin_hints_detect_features( AF_OutlineHints  hints,
+                                  AF_Dimension     dim )
+  {
+    af_latin_hints_compute_segments( hints, dim );
+    af_latin_hints_link_segments   ( hints, dim );
+    af_latin_hints_compute_edges   ( hints dim );
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    af_outline_compute_blue_edges                                      */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Computes the `blue edges' in a given outline (i.e. those that must */
+  /*    be snapped to a blue zone edge (top or bottom).                    */
+  /*                                                                       */
+  FT_LOCAL_DEF( void )
+  af_latin_hints_compute_blue_edges( AF_OutlineHints  outline,
+                                     AF_Face_Globals  face_globals )
+  {
+    AF_Edge     edge       = outline->horz_edges;
+    AF_Edge     edge_limit = edge + outline->num_hedges;
+    AF_Globals  globals    = &face_globals->design;
+    FT_Fixed    y_scale    = outline->y_scale;
+
+    FT_Bool     blue_active[AF_BLUE_MAX];
+
+
+    /* compute which blue zones are active, i.e. have their scaled */
+    /* size < 3/4 pixels                                           */
+    {
+      AF_Blue  blue;
+      FT_Bool  check = 0;
+
+
+      for ( blue = AF_BLUE_CAPITAL_TOP; blue < AF_BLUE_MAX; blue++ )
+      {
+        FT_Pos  ref, shoot, dist;
+
+
+        ref   = globals->blue_refs[blue];
+        shoot = globals->blue_shoots[blue];
+        dist  = ref - shoot;
+        if ( dist < 0 )
+          dist = -dist;
+
+        blue_active[blue] = 0;
+
+        if ( FT_MulFix( dist, y_scale ) < 48 )
+        {
+          blue_active[blue] = 1;
+          check = 1;
+        }
+      }
+
+      /* return immediately if no blue zone is active */
+      if ( !check )
+        return;
+    }
+
+    /* for each horizontal edge search the blue zone which is closest */
+    for ( ; edge < edge_limit; edge++ )
+    {
+      AF_Blue  blue;
+      FT_Pos*  best_blue = 0;
+      FT_Pos   best_dist;  /* initial threshold */
+
+
+      /* compute the initial threshold as a fraction of the EM size */
+      best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale );
+
+#ifdef FT_CONFIG_CHESTER_SMALL_F
+      if ( best_dist > 64 / 2 )
+        best_dist = 64 / 2;
+#else
+      if ( best_dist > 64 / 4 )
+        best_dist = 64 / 4;
+#endif
+
+      for ( blue = AF_BLUE_CAPITAL_TOP; blue < AF_BLUE_MAX; blue++ )
+      {
+        /* if it is a top zone, check for right edges -- if it is a bottom */
+        /* zone, check for left edges                                      */
+        /*                                                                 */
+        /* of course, that's for TrueType XXX                              */
+        FT_Bool  is_top_blue  =
+                   FT_BOOL( AF_IS_TOP_BLUE( blue ) );
+        FT_Bool  is_major_dir =
+                   FT_BOOL( edge->dir == outline->horz_major_dir );
+
+
+        if ( !blue_active[blue] )
+          continue;
+
+        /* if it is a top zone, the edge must be against the major    */
+        /* direction; if it is a bottom zone, it must be in the major */
+        /* direction                                                  */
+        if ( is_top_blue ^ is_major_dir )
+        {
+          FT_Pos   dist;
+          FT_Pos*  blue_pos = globals->blue_refs + blue;
+
+
+          /* first of all, compare it to the reference position */
+          dist = edge->fpos - *blue_pos;
+          if ( dist < 0 )
+            dist = -dist;
+
+          dist = FT_MulFix( dist, y_scale );
+          if ( dist < best_dist )
+          {
+            best_dist = dist;
+            best_blue = blue_pos;
+          }
+
+          /* now, compare it to the overshoot position if the edge is     */
+          /* rounded, and if the edge is over the reference position of a */
+          /* top zone, or under the reference position of a bottom zone   */
+          if ( edge->flags & AF_EDGE_ROUND && dist != 0 )
+          {
+            FT_Bool  is_under_ref = FT_BOOL( edge->fpos < *blue_pos );
+
+
+            if ( is_top_blue ^ is_under_ref )
+            {
+              blue_pos = globals->blue_shoots + blue;
+              dist = edge->fpos - *blue_pos;
+              if ( dist < 0 )
+                dist = -dist;
+
+              dist = FT_MulFix( dist, y_scale );
+              if ( dist < best_dist )
+              {
+                best_dist = dist;
+                best_blue = blue_pos;
+              }
+            }
+          }
+        }
+      }
+
+      if ( best_blue )
+        edge->blue_edge = best_blue;
+    }
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    af_outline_scale_blue_edges                                        */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    This function must be called before hinting in order to re-adjust  */
+  /*    the contents of the detected edges (basically change the `blue     */
+  /*    edge' pointer from `design units' to `scaled ones').               */
+  /*                                                                       */
+  FT_LOCAL_DEF( void )
+  af_outline_hints_scale_blue_edges( AF_OutlineHints  hints )       outline,
+  {
+    AF_AxisHints  axis       = &hints->axis[ AF_DIMENSION_VERT ];
+    AF_Edge       edge       = axis->edges;
+    AF_Edge       edge_limit = edge + axis->num_edges;
+    FT_Pos        delta;
+
+
+    delta = globals->scaled.blue_refs - globals->design.blue_refs;
+
+    for ( ; edge < edge_limit; edge++ )
+    {
+      if ( edge->blue_edge )
+        edge->blue_edge += delta;
+    }
+  }
+
--- /dev/null
+++ b/src/autofit/aflatin.h
@@ -1,0 +1,89 @@
+#ifndef __AFLATIN_H__
+#define __AFLATIN_H__
+
+#include "afhints.h"
+
+FT_BEGIN_HEADER
+ 
+ /* 
+  * the latin-specific script class
+  *
+  */
+  FT_LOCAL( const FT_ScriptClassRec )    af_latin_script_class;
+
+ /*
+  * the following declarations could be embedded in the file "aflatin.c"
+  * they've been made semi-public to allow alternate script hinters to
+  * re-use some of them
+  */
+
+ /*
+  *  Latin (global) metrics management
+  *
+  */
+  
+#define  AF_LATIN_MAX_WIDTHS     16
+#define  AF_LATIN_MAX_BLUES      32
+
+  typedef struct AF_LatinAxisRec_
+  {
+    FT_Fixed     scale;
+    FT_Pos       delta;
+    
+    FT_UInt      width_count;
+    AF_WidthRec  widths[ AF_LATIN_MAX_WIDTHS ];
+    
+   /* ignored for horizontal metrics */
+    FT_Bool      control_overshoot;
+    FT_UInt      blue_count;
+    AF_WidthRec  blue_refs  [ AF_MAX_BLUES ];
+    AF_WidthRec  blue_shoots[ AF_MAX_BLUES ];
+    
+  } AF_LatinAxisRec, *AF_LatinAxis;
+  
+  typedef struct AF_LatinMetricsRec_
+  {
+    AF_OutlineMetricsRec  root;
+    AF_LatinAxisRec       axis[ AF_DIMENSION_MAX ];
+  
+  } AF_LatinMetricsRec, *AF_LatinMetrics;
+
+
+  FT_LOCAL( FT_Error )
+  af_latin_metrics_init( AF_LatinMetrics  metrics,
+                         FT_Face          face );
+
+  FT_LOCAL( void )
+  af_latin_metrics_scale( AF_LatinMetrics  metrics,
+                          AF_Scaler        scaler );
+
+
+ /* 
+  *  Latin (glyph) hints management
+  *
+  */
+
+  FT_LOCAL( 
+
+  FT_LOCAL( void )
+  af_latin_hints_compute_segments( AF_OutlineHints  hints,
+                                   AF_Dimension     dim );
+
+  FT_LOCAL( void )
+  af_latin_hints_link_segments( AF_OutlineHints  hints,
+                                AF_Dimension     dim );
+
+  FT_LOCAL( void )
+  af_latin_hints_compute_edges( AF_OutlineHints  hints,
+                                AF_Dimension     dim );
+
+  FT_LOCAL( void )
+  af_latin_hints_init( AF_OutlineHints  hints,
+                                  AF_Dimension     dim );
+
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFLATIN_H__ */
--- /dev/null
+++ b/src/autofit/aftypes.h
@@ -1,0 +1,289 @@
+#ifndef __AFTYPES_H__
+#define __AFTYPES_H__
+
+FT_BEGIN_HEADER
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                D E B U G G I N G                               *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+
+#define xxAF_DEBUG
+
+#ifdef AF_DEBUG
+
+#  include <stdio.h>
+#  define AF_LOG( x )  printf ## x
+
+#else
+
+#  define AF_LOG( x )  do ; while ( 0 ) /* nothing */
+
+#endif /* AF_DEBUG */
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                A N G L E   T Y P E S                           *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+ 
+ /*
+  *  Angle type. The auto-fitter doesn't need a very high angular accuracy,
+  *  and this allows us to speed up some computations considerably with a
+  *  light Cordic algorithm (see afangle.c)
+  *
+  */
+
+  typedef FT_Int    AF_Angle;
+
+#define  AF_ANGLE_PI     128
+#define  AF_ANGLE_2PI    (AF_ANGLE_PI*2)
+#define  AF_ANGLE_PI2    (AF_ANGLE_PI/2)
+#define  AF_ANGLE_PI4    (AF_ANGLE_PI/4)
+
+ /*
+  *  compute the angle of a given 2-D vector
+  *
+  */
+  FT_LOCAL( AF_Angle )
+  af_angle( FT_Pos  dx,
+            FT_Pos  dy );
+
+
+ /*
+  *  computes "angle2 - angle1", the result is always within
+  *  the range [ -AF_ANGLE_PI .. AF_ANGLE_PI-1 ]
+  *
+  */
+  FT_LOCAL( AF_Angle )
+  af_angle_diff( AF_Angle  angle1,
+                 AF_Angle  angle2 );
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                O U T L I N E S                                 *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+
+  typedef struct AF_OutlineHintsRec_*  AF_OutlineHints;
+
+  typedef struct AF_GlobalHintsRec_*   AF_GlobalHints;
+
+  typedef struct AF_OutlineRec_
+  {
+    FT_Memory        memory;
+    FT_Face          face;
+    FT_OutlineRec    outline;
+    FT_UInt          outline_resolution;
+    
+    FT_Int           advance;
+    FT_UInt          metrics_resolution;
+    
+    AF_OutlineHints  hints;
+
+  } AF_OutlineRec;
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                G L O B A L   M E T R I C S                     *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+
+  /*
+   * the following define global metrics in a _single_ dimension
+   *
+   * the "blue_refs" and "blue_shoots" arrays are ignored in
+   * the horizontal dimension
+   */
+
+  typedef struct AF_WidthRec_
+  {
+    FT_Pos  org;  /* original position/width in font units              */
+    FT_Pos  cur;  /* current/scaled position/width in device sub-pixels */
+    FT_Pos  fit;  /* current/fitted position/width in device sub-pixels */
+  
+  } AF_WidthRec, *AF_Width;
+  
+
+#define  AF_MAX_WIDTHS     16
+#define  AF_MAX_BLUES      32
+
+  typedef struct  AF_GlobalMetricsRec_
+  {
+    FT_Int       num_widths;
+    AF_WidthRec  widths[ AF_MAX_WIDTHS ];
+
+    FT_Fixed     scale;  /* used to scale from org to cur with:   */
+    FT_Pos       delta;  /*   x_cur = x_org * scale + delta       */
+
+    /* ignored for horizontal metrics */
+    AF_WidthRec  blue_refs  [ AF_MAX_BLUES ];
+    AF_WidthRec  blue_shoots[ AF_MAX_BLUES ];
+
+    FT_Bool      control_overshoot;
+
+  } AF_GlobalMetricsRec, *AF_GlobalMetrics;
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                S C A L E R S                                   *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+
+ /*
+  *  A scaler models the target pixel device that will receive the
+  *  auto-hinted glyph image
+  *
+  */
+  
+  typedef enum
+  {
+    AF_SCALER_FLAG_NO_HORIZONTAL = 1,  /* disable horizontal hinting */
+    AF_SCALER_FLAG_NO_VERTICAL   = 2,  /* disable vertical hinting   */
+    AF_SCALER_FLAG_NO_ADVANCE    = 4   /* disable advance hinting    */
+
+  } AF_ScalerFlags;
+
+
+  typedef struct AF_ScalerRec_
+  {
+    FT_Face         face;         /* source font face                        */
+    FT_Fixed        x_scale;      /* from font units to 1/64th device pixels */
+    FT_Fixed        y_scale;      /* from font units to 1/64th device pixels */
+    FT_Pos          x_delta;      /* in 1/64th device pixels                 */
+    FT_Pos          y_delta;      /* in 1/64th device pixels                 */
+    FT_Render_Mode  render_mode;  /* monochrome, anti-aliased, LCD, etc..    */
+    FT_UInt32       flags;        /* additionnal control flags, see above    */
+  
+  } AF_ScalerRec, *AF_Scaler;
+
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                S C R I P T S                                   *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+
+ /*
+  *  the list of know scripts. Each different script correspond to the
+  *  following information:
+  *
+  *   - a set of Unicode ranges to test wether the face supports the
+  *     script
+  *
+  *   - a specific global analyzer that will compute global metrics
+  *     specific to the script.
+  *
+  *   - a specific hinting routine
+  *
+  *  all scripts should share the same analysis routine though
+  */
+  typedef enum
+  {
+    AF_SCRIPT_LATIN = 0,
+    /* add new scripts here */
+    
+    AF_SCRIPT_MAX   /* do not remove */
+  
+  } AF_Script;
+
+
+  typedef struct AF_ScriptClassRec_ const*  AF_ScriptClass;
+
+ /*
+  *  root class for script-specific metrics
+  */
+  typedef struct AF_ScriptMetricsRec_
+  {
+    AF_ScriptClass        script_class;
+    AF_GlobalMetricsRec   horz_metrics;
+    AF_GlobalMetricsRec   vert_metrics;
+
+  } AF_ScriptMetricsRec, *AF_ScriptMetrics;
+
+
+ /* this function parses a FT_Face to compute global metrics for
+  * a specific script
+  */ 
+  typedef FT_Error  (*AF_Script_InitMetricsFunc)( AF_ScriptMetrics  metrics,
+                                                  FT_Face           face );
+
+  typedef void      (*AF_Script_ScaleMetricsFunc)( AF_ScriptMetrics  metrics,
+                                                   AF_Scaler         scaler );
+
+  typedef void      (*AF_Script_DoneMetricsFunc)( AF_ScriptMetrics  metrics );
+
+
+  typedef FT_Error  (*AF_Script_InitHintsFunc)( AF_OutlineHints   hints,
+                                                AF_Scaler         scaler,
+                                                AF_ScriptMetrics  metrics );
+
+  typedef void      (*AF_Script_ApplyHintsFunc)( AF_OutlineHints  hints );
+                                                 
+
+  typedef struct AF_Script_UniRangeRec_
+  {
+    FT_UInt32    first;
+    FT_UInt32    last;
+  
+  } AF_Script_UniRangeRec, *AF_Script_UniRange;
+ 
+
+  typedef struct AF_ScriptClassRec_
+  {
+    AF_Script                   script;
+    AF_Scipt_UniRange           script_uni_ranges;  /* last must be { 0, 0 } */
+
+    FT_UInt                     script_metrics_size;
+    AF_Script_InitMetricsFunc   script_metrics_init;
+    AF_Script_ScaleMetricsFunc  script_metrics_scale;
+    AF_Script_DoneMetricsFunc   script_metrics_done;
+
+  } AF_ScriptClassRec;
+
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /*****                                                                *****/
+ /*****                F A C E   G L O B A L S                         *****/
+ /*****                                                                *****/
+ /**************************************************************************/
+ /**************************************************************************/
+
+ /*
+  *  models the global hints data for a given face, decomposed into
+  *  script-specific items..
+  *
+  */
+  typedef struct AF_FaceGlobalsRec_
+  {
+    FT_Face               face;
+    FT_UInt               glyph_count;    /* same as face->num_glyphs     */
+    FT_Byte*              glyph_scripts;  /* maps each gindex to a script */
+    
+    FT_ScriptMetrics      metrics[ AF_SCRIPT_MAX ];
+
+  } AF_FaceGlobalsRec, *AF_FaceGlobals;
+ 
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFTYPES_H__ */
--- a/src/base/ftoutln.c
+++ b/src/base/ftoutln.c
@@ -26,6 +26,7 @@
 #include <ft2build.h>
 #include FT_OUTLINE_H
 #include FT_INTERNAL_OBJECTS_H
+#include FT_TRIGONOMETRY_H
 
 
   /*************************************************************************/
@@ -654,5 +655,141 @@
       FT_Vector_Transform( vec, matrix );
   }
 
+
+
+  typedef FT_OrientationExtremumRec_
+  {
+    FT_Int   index;
+    FT_Int   pos;
+    FT_Int   first;
+    FT_Int   last;
+  
+  } FT_OrientationExtremumRec;
+
+ 
+  static FT_Orientation
+  ft_orientation_extremum_compute( FT_OrientationExtremumRec*  extremum,
+                                   FT_Outline*                 outline )
+  {
+    FT_Vector  *point, *first, *last, *prev, *next;
+    FT_Vector*  points = outline->points;
+    FT_Angle    angle_in, angle_out;
+    
+   /* compute the previous and next points in the same contour
+    */
+    point = points + extremum->index;
+    first = points + extremum->first;
+    last  = points + extremum->last;
+   
+    do
+    {
+      prev = ( point == first ) ? last : point-1;
+     
+      if ( prev == point )
+        return FT_ORIENTATION_TRUETYPE;  /* degenerate case */
+      
+    } while ( prev->x != point->x || prev->y != point->y );
+    
+    do
+    {
+      next = ( point == last ) ? first : point+1;
+      
+      if ( next == point )
+        return FT_ORIENTATION_TRUETYPE;  /* shouldn't happen */
+        
+    } while ( next->x != point->x || next->y != point->y );
+    
+   /* now, compute the orientation of the "out" vector relative
+    * to the "in" vector.
+    */
+    angle_in  = FT_Atan2( point->x - prev->x,  point->y - prev->y );
+    angle_out = FT_Atan2( next->x  - point->x, next->y  - point->y );
+    
+    return ( FT_Angle_Diff( angle_in, angle_out ) >= 0 )
+           ? FT_ORIENTATION_TRUETYPE
+           : FT_ORIENTATION_POSTSCRIPT;
+  }
+
+
+  FT_EXPORT_DEF( FT_Orientation )
+  FT_Outline_Get_Orientation( FT_Outline*  outline )
+  {
+    FT_Orientation  result = FT_ORIENTATION_TRUETYPE;
+    
+    if ( outline && outline->n_points > 0 )
+    {
+      FT_OrientationExtremumRec  xmin, ymin, xmax, ymax;
+      FT_Int                     n;
+      FT_Int                     first, last;
+      FT_Vector*                 points = outline->points;
+      
+      xmin.pos = ymin.pos = +32768L;
+      xmax.pos = ymax.pos = -32768L;
+      
+      xmin.index = ymin.index = xmax.index = ymax.index = -1;
+
+      first = 0;
+      for ( n = 0; n < outline->n_contours; n++, first = last+1 )
+      {
+        last = outline->contours[n];
+
+       /* skip single-point contours, these are degenerated cases
+        */
+        if ( last > first+1 )
+        {
+          FT_Int  i;
+          
+          for ( i = first; i < last; i++ )
+          {
+            x = points[i].x;
+            y = points[i].y;
+            
+            if ( x < xmin.pos )
+            {
+              xmin.pos   = x;
+              xmin.index = i;
+              xmin.first = first;
+              xmin.last  = last;
+            }
+            if ( x > xmax.pos )
+            {
+              xmax.pos   = x;
+              xmax.index = i;
+              xmax.first = first;
+              xmax.last  = last;
+            }
+            if ( y < ymin.pos )
+            {
+              ymin.pos   = y;
+              ymin.index = i;
+              ymin.first = first;
+              ymin.last  = last;
+            }
+            if ( y > ymax.pos )
+            {
+              ymax.pos   = y;
+              ymax.index = i;
+              ymax.first = first;
+              ymax.last  = last;
+            }
+          }
+        }
+        
+        if ( xmin.index >= 0 )
+          result = ft_orientation_extremum_compute( &xmin, outline );
+          
+        else if ( xmax.index >= 0 )
+          result = ft_orientation_extremum_compute( &xmax, outline );
+          
+        else if ( ymin.index >= 0 )
+          result = ft_orientation_extremum_compute( &ymin, outline );
+          
+        else if ( ymax.index >= 0 )
+          result = ft_orientation_extremum_compute( &ymax, outline );
+      }
+    }
+    
+    return result;
+  }
 
 /* END */