shithub: freetype+ttf2subf

Download patch

ref: 3469d0d038b01c7ddce0fb67da68d79ad2f8b8a6
parent: 6930b45f78abc193e84544f804cead9436b86ad5
author: David Turner <[email protected]>
date: Wed Jul 19 16:02:14 EDT 2000

added auto-hinter module. Note that the code has been
cleaned up, and it seems a bug was introduced ???

I'll start checking this under Linux, as debugging is a lot
easier under this environment..

git/fs: mount .git/fs: mount/attach disallowed
--- /dev/null
+++ b/src/autohint/CatharonLicense.txt
@@ -1,0 +1,123 @@
+                  The Catharon Open Source LICENSE
+                    ----------------------------
+
+                            2000-Jul-4
+
+          Copyright (C) 2000 by Catharon Productions, Inc.
+ 
+
+
+Introduction
+============
+
+  This license  applies  to  source  files  distributed  by Catharon
+  Productions,  Inc.  in  several  archive  packages.  This  license
+  applies to all files found in such  packages  which  do  not  fall
+  under their own explicit license.
+
+  This  license   was  inspired  by  the  BSD,   Artistic,  and  IJG
+  (Independent JPEG  Group) licenses, which  all encourage inclusion
+  and  use of  free  software in  commercial  and freeware  products
+  alike.  As a consequence, its main points are that:
+
+    o We don't promise that this software works.  However, we are 
+      interested in any kind of bug reports. (`as is' distribution)
+
+    o You can  use this software for whatever you  want, in parts or
+      full form, without having to pay us. (`royalty-free' usage)
+
+    o You may not pretend that  you wrote this software.  If you use
+      it, or  only parts of it,  in a program,  you must acknowledge
+      somewhere  in  your  documentation  that  you  have  used  the
+      Catharon Code. (`credits')
+
+  We  specifically  permit  and  encourage  the  inclusion  of  this
+  software,  with  or without modifications, in commercial products.
+  We disclaim all  warranties  covering  the packages distributed by
+  Catharon Productions, Inc. and  assume  no  liability  related  to
+  their use.
+
+
+Legal Terms
+===========
+
+0. Definitions
+--------------
+
+  Throughout  this license, the terms `Catharon Package', `package',
+  and  `Catharon  Code'  refer  to   the  set  of  files  originally
+  distributed by Catharon Productions, Inc.
+
+  `You' refers to the licensee, or person using the  project,  where
+  `using' is a generic term including compiling the project's source
+  code  as  well  as linking it to form a `program' or `executable'.
+  This program  is  referred  to  as  `a  program  using  one of the
+  Catharon Packages'.
+
+  This license applies to all  files  distributed  in  the  original
+  Catharon  Package(s),  including  all  source  code,  binaries and
+  documentation,  unless  otherwise  stated   in  the  file  in  its
+  original, unmodified form as distributed in the original  archive.
+  If  you  are unsure whether or not a particular file is covered by
+  this license, you must contact us to verify this.
+
+  The  Catharon  Packages  are   copyright   (C)  2000  by  Catharon
+  Productions, Inc. All rights reserved except as specified below.
+
+1. No Warranty
+--------------
+
+  THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
+  KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT  NOT  LIMITED  TO,
+  WARRANTIES   OF  MERCHANTABILITY  AND  FITNESS  FOR  A  PARTICULAR
+  PURPOSE.  IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO
+  USE THE CATHARON PACKAGE.
+
+2. Redistribution
+-----------------
+
+  This license  grants  a  worldwide,  royalty-free,  perpetual  and
+  irrevocable  right  and license to use, execute, perform, compile,
+  display,  copy,  create   derivative   works  of,  distribute  and
+  sublicense the Catharon Packages (in both source and  object  code
+  forms)  and  derivative  works  thereof  for  any  purpose; and to
+  authorize others to exercise  some  or  all  of the rights granted
+  herein, subject to the following conditions:
+
+    o Redistribution  of source code  must retain this  license file
+      (`license.txt') unaltered; any additions, deletions or changes
+      to   the  original   files  must   be  clearly   indicated  in
+      accompanying  documentation.   The  copyright notices  of  the
+      unaltered, original  files must be preserved in  all copies of
+      source files.
+
+    o Redistribution in binary form must provide a  disclaimer  that
+      states  that  the  software  is  based  in part on the work of
+      Catharon Productions, Inc. in  the  distribution documentation.
+
+  These  conditions  apply  to any software derived from or based on
+  the Catharon Packages, not just  the unmodified files.  If you use
+  our work, you must acknowledge us.  However, no fee need  be  paid
+  to us.
+
+3. Advertising
+--------------
+
+  Neither Catharon Productions, Inc.  and contributors nor you shall
+  use  the  name  of  the  other  for  commercial,  advertising,  or
+  promotional purposes without specific prior written permission.
+
+  We suggest, but do not require, that you use the following  phrase
+  to refer to this software in your documentation: 'this software is
+  based in part on the Catharon Typography Project'.
+
+  As you have not signed this  license,  you  are  not  required  to
+  accept  it.   However,  as  the  Catharon Packages are copyrighted
+  material, only this license,  or  another  one contracted with the
+  authors, grants you the right to use, distribute, and  modify  it.
+  Therefore,  by  using,  distributing,  or  modifying  the Catharon
+  Packages, you indicate  that  you  understand  and  accept all the
+  terms of this license.
+
+--- end of license.txt ---
--- /dev/null
+++ b/src/autohint/ahangles.c
@@ -1,0 +1,125 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahangles.h                                                             */
+/*                                                                         */
+/*    a routine used to compute vector angles with limited accuracy        */
+/*    and very high speed.                                                 */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifdef FT_FLAT_COMPILE
+#include "ahangles.h"
+#else
+#include <autohint/ahangles.h>
+#endif
+
+/* the following two tables are automatically generated with */
+/* the "mather.py" Python script..                           */
+
+static const AH_Angle  ah_arctan[ 1L << AH_ATAN_BITS ] =
+{
+ 0, 0, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 5,
+ 5, 5, 6, 6, 6, 7, 7, 7,
+ 8, 8, 8, 9, 9, 9, 10, 10,
+ 10, 10, 11, 11, 11, 12, 12, 12,
+ 13, 13, 13, 14, 14, 14, 14, 15,
+ 15, 15, 16, 16, 16, 17, 17, 17,
+ 18, 18, 18, 18, 19, 19, 19, 20,
+ 20, 20, 21, 21, 21, 21, 22, 22,
+ 22, 23, 23, 23, 24, 24, 24, 24,
+ 25, 25, 25, 26, 26, 26, 26, 27,
+ 27, 27, 28, 28, 28, 28, 29, 29,
+ 29, 30, 30, 30, 30, 31, 31, 31,
+ 31, 32, 32, 32, 33, 33, 33, 33,
+ 34, 34, 34, 34, 35, 35, 35, 35,
+ 36, 36, 36, 36, 37, 37, 37, 38,
+ 38, 38, 38, 39, 39, 39, 39, 40,
+ 40, 40, 40, 41, 41, 41, 41, 42,
+ 42, 42, 42, 42, 43, 43, 43, 43,
+ 44, 44, 44, 44, 45, 45, 45, 45,
+ 46, 46, 46, 46, 46, 47, 47, 47,
+ 47, 48, 48, 48, 48, 48, 49, 49,
+ 49, 49, 50, 50, 50, 50, 50, 51,
+ 51, 51, 51, 51, 52, 52, 52, 52,
+ 52, 53, 53, 53, 53, 53, 54, 54,
+ 54, 54, 54, 55, 55, 55, 55, 55,
+ 56, 56, 56, 56, 56, 57, 57, 57,
+ 57, 57, 57, 58, 58, 58, 58, 58,
+ 59, 59, 59, 59, 59, 59, 60, 60,
+ 60, 60, 60, 61, 61, 61, 61, 61,
+ 61, 62, 62, 62, 62, 62, 62, 63,
+ 63, 63, 63, 63, 63, 64, 64, 64
+};
+
+
+  LOCAL_FUNC
+  AH_Angle    ah_angle( FT_Vector*  v )
+  {
+    FT_Pos    dx, dy;
+    AH_Angle  angle;
+
+    dx    = v->x;
+    dy    = v->y;
+
+    /* check trivial cases */
+    if (dy == 0)
+    {
+      angle = 0;
+      if (dx < 0)
+        angle = AH_PI;
+      return angle;
+    }
+    else if (dx == 0)
+    {
+      angle = AH_HALF_PI;
+      if (dy < 0)
+        angle = -AH_HALF_PI;
+      return angle;
+    }
+
+    angle = 0;
+    if ( dx < 0 )
+    {
+      dx = -v->x;
+      dy = -v->y;
+      angle = AH_PI;
+    }
+
+    if ( dy < 0 )
+    {
+      FT_Pos  tmp;
+      tmp = dx;
+      dx  = -dy;
+      dy  = tmp;
+      angle -= AH_HALF_PI;
+    }
+
+    if (dx == 0 && dy == 0)
+      return 0;
+
+    if (dx == dy)
+      angle += AH_PI/4;
+    else if (dx > dy)
+      angle += ah_arctan[ FT_DivFix( dy, dx ) >> (16-AH_ATAN_BITS) ];
+    else
+      angle += AH_HALF_PI - ah_arctan[ FT_DivFix( dx, dy ) >> (16-AH_ATAN_BITS) ];
+
+    if (angle > AH_PI)
+      angle -= AH_2PI;
+
+    return angle;
+  }
+
+
--- /dev/null
+++ b/src/autohint/ahangles.h
@@ -1,0 +1,47 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahangles.h                                                             */
+/*                                                                         */
+/*    a routine used to compute vector angles with limited accuracy        */
+/*    and very high speed.                                                 */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifndef AGANGLES_H
+#define AGANGLES_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+#include <freetype/internal/ftobjs.h>
+
+/* PI expressed in ah_angles - we don't really need an important */
+/* precision, so 256 should be enough..                          */
+#define AH_PI         256
+#define AH_2PI        (AH_PI*2)
+#define AH_HALF_PI    (AH_PI/2)
+#define AH_2PIMASK    (AH_2PI-1)
+
+/* the number of bits to use to express an arc tangent */
+/* see the structure of the lookup table..             */
+#define AH_ATAN_BITS  8
+
+LOCAL_DEF  const AH_Angle    ah_arctan[ 1L << AH_ATAN_BITS ];
+
+LOCAL_DEF  AH_Angle    ah_angle( FT_Vector* v );
+
+#endif /* AGANGLES_H */
--- /dev/null
+++ b/src/autohint/ahglobal.c
@@ -1,0 +1,365 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahglobal.c                                                             */
+/*                                                                         */
+/*    routines used to compute global metrics automatically                */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifdef FT_FLAT_COMPILE
+#include "ahglobal.h"
+#include "ahglyph.h"
+#else
+#include <autohint/ahglobal.h>
+#include <autohint/ahglyph.h>
+#endif
+
+#define MAX_TEST_CHARACTERS 12
+
+  static const char* blue_chars[ ah_blue_max ] =
+    {
+      "THEZOCQS",
+      "HEZLOCUS",
+      "xzroesc",
+      "xzroesc",
+      "pqgjy"
+    };
+
+  /* simple insertion sort */
+  static
+  void  sort_values( FT_Int  count, FT_Pos*  table )
+  {
+    FT_Int  i, j, swap;
+
+    for ( i = 1; i < count; i++ )
+    {
+      for ( j = i; j > 1; j-- )
+      {
+        if ( table[j] > table[j-1] )
+          break;
+
+        swap       = table[j];
+        table[j]   = table[j-1];
+        table[j-1] = swap;
+      }
+    }
+  }
+
+
+  static
+  FT_Error  ah_hinter_compute_blues( AH_Hinter*  hinter )
+  {
+    AH_Blue      blue;
+    AH_Globals*  globals = &hinter->globals->design;
+    FT_Pos       flats [ MAX_TEST_CHARACTERS ];
+    FT_Pos       rounds[ MAX_TEST_CHARACTERS ];
+    FT_Int       num_flats;
+    FT_Int       num_rounds;
+
+    FT_Face       face;
+    FT_GlyphSlot  glyph;
+    FT_Error      error;
+    FT_CharMap    charmap;
+
+    face  = hinter->face;
+    glyph = face->glyph;
+
+    /* save current charmap */
+    charmap = face->charmap;
+
+    /* do we have a Unicode charmap in there ?? */
+    error = FT_Select_Charmap( face, ft_encoding_unicode );
+    if (error) goto Exit;
+
+    /* we compute the blues simply by loading each character from the        */
+    /* 'blue_chars[blues]' string, then compute its top-most and bottom-most */
+    /* points                                                                */
+
+    AH_LOG(( "blue zones computation\n" ));
+    AH_LOG(( "------------------------------------------------\n" ));
+
+    for ( blue = (AH_Blue)0; blue < ah_blue_max; blue++ )
+    {
+      const char*  p     = blue_chars[blue];
+      const char*  limit = p + MAX_TEST_CHARACTERS;
+      FT_Pos      *blue_ref, *blue_shoot;
+
+      AH_LOG(( "blue %3d : ", (int)blue ));
+
+      num_flats  = 0;
+      num_rounds = 0;
+      for ( ; p < limit; p++ )
+      {
+        FT_UInt        glyph_index;
+        FT_Vector*     extremum;
+        FT_Vector*     points;
+        FT_Vector*     point_limit;
+        FT_Vector*     point;
+        FT_Bool        round;
+
+        /* exit if we reach the end of the string */
+        if (!*p) break;
+
+        AH_LOG(( "'%c'", *p ));
+
+        /* load the character in the face - skip unknown or empty ones */
+        glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
+        if (glyph_index == 0) continue;
+
+        error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+        if (error || glyph->outline.n_points <= 0) continue;
+
+
+        /* now compute min or max point indices and coordinates */
+        points      = glyph->outline.points;
+        point_limit = points + glyph->outline.n_points;
+        point       = points;
+        extremum    = point;
+        point++;
+
+        if ( AH_IS_TOP_BLUE(blue) )
+        {
+          for ( ; point < point_limit; point++ )
+            if ( point->y > extremum->y )
+              extremum = point;
+        }
+        else
+        {
+          for ( ; point < point_limit; point++ )
+            if ( point->y < extremum->y )
+              extremum = point;
+        }
+
+        AH_LOG(( "%5d", (int)extremum->y ));
+
+        /* now, see if the point belongs to a straight or round segment  */
+        /* we first need to find in which contour the extremum lies then */
+        /* see its previous and next points..                            */
+        {
+          FT_Int  index = extremum - points;
+          FT_Int  n;
+          FT_Int  first = 0;
+          FT_Int  last, prev, next, end;
+          FT_Pos  dist;
+
+          last  = -1;
+          first = 0;
+          for ( n = 0; n < glyph->outline.n_contours; n++ )
+          {
+            end = glyph->outline.contours[n];
+            if ( end >= index )
+            {
+              last = end;
+              break;
+            }
+            first = end+1;
+          }
+
+          /* XXX : should never happen !!! */
+          if ( last < 0 )
+            continue;
+
+          /* now look for the previous and next points that are not on the */
+          /* same Y coordinate. Threshold the "closeness" ..               */
+
+          prev = index;
+          next = prev;
+
+          do
+          {
+            if (prev > first) prev--;
+                         else prev = last;
+
+            dist = points[prev].y - extremum->y;
+            if ( dist < -5 || dist > 5 )
+              break;
+
+          } while (prev != index);
+
+          do
+          {
+            if (next < last)  next++;
+                         else next = first;
+
+            dist = points[next].y - extremum->y;
+            if ( dist < -5 || dist > 5 )
+              break;
+
+          } while (next != index);
+
+          /* now, set the "round" flag depending on the segment's kind */
+          round = FT_CURVE_TAG(glyph->outline.tags[prev]) != FT_Curve_Tag_On ||
+                  FT_CURVE_TAG(glyph->outline.tags[next]) != FT_Curve_Tag_On ;
+
+          AH_LOG(( "%c ", round ? 'r' : 'f' ));
+        }
+
+        if (round)
+          rounds[ num_rounds++ ] = extremum->y;
+        else
+          flats[ num_flats++ ] = extremum->y;
+      }
+
+      AH_LOG(( "\n" ));
+      /* we have computed the contents of the 'rounds' and 'flats' tables */
+      /* now determine the reference and overshoot position of the blue   */
+      /* we simply take the median value after a simple short..           */
+      sort_values( num_rounds, rounds );
+      sort_values( num_flats,  flats  );
+
+      blue_ref   = globals->blue_refs + blue;
+      blue_shoot = globals->blue_shoots + blue;
+      if ( num_flats == 0 && num_rounds == 0 )
+      {
+        *blue_ref   = -10000;
+        *blue_shoot = -10000;
+      }
+      else if ( num_flats == 0 )
+      {
+        *blue_ref   =
+        *blue_shoot = rounds[ num_rounds/2 ];
+      }
+      else if ( num_rounds == 0 )
+      {
+        *blue_ref   =
+        *blue_shoot = flats[ num_flats/2 ];
+      }
+      else
+      {
+        *blue_ref   = flats[ num_flats/2 ];
+        *blue_shoot = rounds[ num_rounds/2 ];
+      }
+
+      /* there are sometimes problems, when the overshoot position of top    */
+      /* zones is under its reference position, or the opposite for bottom   */
+      /* zones. We must thus check everything there.. and correct the errors */
+      if ( *blue_shoot != *blue_ref )
+      {
+        FT_Pos   ref       = *blue_ref;
+        FT_Pos   shoot     = *blue_shoot;
+        FT_Bool  over_ref = ( shoot > ref );
+
+        if ( AH_IS_TOP_BLUE(blue) ^ over_ref )
+          *blue_shoot = *blue_ref = (shoot+ref)/2;
+      }
+      AH_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
+    }
+
+    /* reset original face charmap */
+    FT_Set_Charmap( face, charmap );
+    error = 0;
+
+  Exit:
+    return error;
+  }
+
+
+  static
+  FT_Error  ah_hinter_compute_widths( AH_Hinter*  hinter )
+  {
+    /* scan the array of segments in each direction */
+    AH_Outline*    outline = hinter->glyph;
+    AH_Segment*    segments;
+    AH_Segment*    limit;
+    AH_Globals*    globals = &hinter->globals->design;
+    FT_Pos*        widths;
+    FT_Int         dimension;
+    FT_Int*        p_num_widths;
+    FT_Error       error = 0;
+    FT_Pos         edge_distance_threshold = 32000;
+
+    globals->num_widths  = 0;
+    globals->num_heights = 0;
+
+    /* for now, compute the standard width and height from the "o" character */
+    /* I started computing the stem width of the "i" and the stem height of  */
+    /* the "-", but it wasn't too good.. Moreover, we now have a single      */
+    /* character that gives us standard width and height                     */
+    {
+      FT_UInt   glyph_index;
+
+      glyph_index = FT_Get_Char_Index( hinter->face, 'o' );
+      if (glyph_index == 0) return 0;
+
+      error = FT_Load_Glyph( hinter->face, glyph_index, FT_LOAD_NO_SCALE );
+      if (error) goto Exit;
+
+      error = ah_outline_load( hinter->glyph, hinter->face );
+      if (error) goto Exit;
+
+      ah_outline_compute_segments( hinter->glyph );
+      ah_outline_link_segments( hinter->glyph );
+    }
+
+    segments     = outline->horz_segments;
+    limit        = segments + outline->num_hsegments;
+    widths       = globals->heights;
+    p_num_widths = &globals->num_heights;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Segment*  seg = segments;
+      AH_Segment*  link;
+      FT_Int       num_widths = 0;
+
+      for ( ; seg < limit; seg++ )
+      {
+        link = seg->link;
+        /* we only consider the stem segments there ! */
+        if (link && link->link == seg && link > seg)
+        {
+          FT_Int  dist;
+
+          dist = seg->pos - link->pos;
+          if (dist < 0) dist = -dist;
+
+          if ( num_widths < 12 )
+            widths[ num_widths++ ] = dist;
+        }
+      }
+
+      sort_values( num_widths, widths );
+      *p_num_widths = num_widths;
+
+      /* we will now try to find the smallest width */
+      if (num_widths > 0 && widths[0] < edge_distance_threshold )
+        edge_distance_threshold = widths[0];
+
+      segments     = outline->vert_segments;
+      limit        = segments + outline->num_vsegments;
+      widths       = globals->widths;
+      p_num_widths = &globals->num_widths;
+
+    }
+
+    /* now, compute the edge distance threshold as a fraction of the */
+    /* smallest width in the font.. Set it in "hinter.glyph" too !!  */
+    if ( edge_distance_threshold == 32000)
+      edge_distance_threshold = 50;
+
+    /* let's try 20% */
+    hinter->glyph->edge_distance_threshold = edge_distance_threshold/5;
+
+  Exit:
+    return error;
+  }
+
+
+  LOCAL_FUNC
+  FT_Error  ah_hinter_compute_globals( AH_Hinter*  hinter )
+  {
+    return ah_hinter_compute_widths( hinter ) ||
+           ah_hinter_compute_blues ( hinter );
+  }
+
--- /dev/null
+++ b/src/autohint/ahglobal.h
@@ -1,0 +1,38 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahglobal.h                                                             */
+/*                                                                         */
+/*    routines used to compute global metrics automatically                */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifndef AGGLOBAL_H
+#define AGGLOBAL_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+#include <freetype/internal/ftobjs.h>  /* for LOCAL_DEF/LOCAL_FUNC */
+
+#define  AH_IS_TOP_BLUE(b)   ( (b) == ah_blue_capital_top || \
+                               (b) == ah_blue_small_top   )
+
+ /* compute global metrics automatically */
+  LOCAL_DEF
+  FT_Error  ah_hinter_compute_globals( AH_Hinter*  hinter );
+
+#endif /* AGGLOBAL_H */
--- /dev/null
+++ b/src/autohint/ahglyph.c
@@ -1,0 +1,1192 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahglyph.c                                                              */
+/*                                                                         */
+/*    routines used to load and analyze a given glyph before hinting       */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+
+#ifdef FT_FLAT_COMPILE
+#include "ahglyph.h"
+#include "ahangles.h"
+#include "ahglobal.h"
+#else
+#include <autohint/ahglyph.h>
+#include <autohint/ahangles.h>
+#include <autohint/ahglobal.h>
+#endif
+
+#include <stdio.h>
+
+#define xxxAH_DEBUG_GLYPH
+
+ /* compute the direction value of a given vector.. */
+  static
+  AH_Direction  ah_compute_direction( FT_Pos  dx, FT_Pos  dy )
+  {
+    AH_Direction  dir;
+    FT_Pos        ax = ABS(dx);
+    FT_Pos        ay = ABS(dy);
+
+    dir = ah_dir_none;
+
+    /* test for vertical direction */
+    if ( ax*12 < ay )
+    {
+      dir = ( dy > 0 ? ah_dir_up : ah_dir_down );
+    }
+    /* test for horizontal direction */
+    else if ( ay*12 < ax )
+    {
+      dir = ( dx > 0 ? ah_dir_right : ah_dir_left );
+    }
+
+    return dir;
+  }
+
+ /************************************************************************
+  *
+  * <Function>
+  *    ah_outline_new
+  *
+  * <Description>
+  *    Create a new empty AH_Outline
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  FT_Error  ah_outline_new( FT_Memory    memory,
+                            AH_Outline* *aoutline )
+  {
+    FT_Error     error;
+    AH_Outline*  outline;
+
+    if ( !ALLOC( outline, sizeof(*outline) ) )
+    {
+      outline->memory = memory;
+      *aoutline = outline;
+    }
+    return error;
+  }
+
+
+ /************************************************************************
+  *
+  * <Function>
+  *    ah_outline_done
+  *
+  * <Description>
+  *    Destroys a given AH_Outline
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  void  ah_outline_done( AH_Outline*  outline )
+  {
+    FT_Memory memory = outline->memory;
+
+    FREE( outline->horz_edges );
+    FREE( outline->horz_segments );
+    FREE( outline->contours );
+    FREE( outline->points );
+    outline->vert_edges    = 0;
+    outline->vert_segments = 0;
+
+    outline->num_points   = 0;
+    outline->max_points   = 0;
+    outline->num_contours = 0;
+    outline->max_contours = 0;
+    FREE( outline );
+  }
+
+
+
+ /************************************************************************
+  *
+  * <Function>
+  *    ah_outline_save
+  *
+  * <Description>
+  *    Save the content of a given AH_Outline into a face's glyph slot
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  void  ah_outline_save( AH_Outline*  outline,
+                         AH_Loader*   gloader )
+  {
+    AH_Point*   point = outline->points;
+    AH_Point*   limit = point + outline->num_points;
+    FT_Vector*  vec   = gloader->current.outline.points;
+    char*       tag   = gloader->current.outline.tags;
+
+    /* we assume that the glyph loader has already been checked for storage */
+    for ( ; point < limit; point++, vec++, tag++ )
+    {
+      vec->x = point->x;
+      vec->y = point->y;
+
+      if (point->flags & ah_flah_conic)
+        tag[0] = FT_Curve_Tag_Conic;
+      else if (point->flags & ah_flah_cubic)
+        tag[0] = FT_Curve_Tag_Cubic;
+      else
+        tag[0] = FT_Curve_Tag_On;
+    }
+  }
+
+
+ /************************************************************************
+  *
+  * <Function>
+  *    ah_outline_load
+  *
+  * <Description>
+  *    Loads an unscaled outline from a glyph slot into an AH_Outline
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  FT_Error  ah_outline_load( AH_Outline*  outline,
+                             FT_Face      face )
+  {
+    FT_Memory   memory       = outline->memory;
+    FT_Error    error        = FT_Err_Ok;
+    FT_Outline* source       = &face->glyph->outline;
+    FT_Int      num_points   = source->n_points;
+    FT_Int      num_contours = source->n_contours;
+    AH_Point*   points;
+
+    /* check arguments */
+    if (!face || !face->size || face->glyph->format != ft_glyph_format_outline)
+      return FT_Err_Invalid_Argument;
+
+
+    /* first of all, realloc the contours array if necessary */
+    if ( num_contours > outline->max_contours )
+    {
+      FT_Int  new_contours = (num_contours+3) & -4;
+
+      if ( REALLOC_ARRAY( outline->contours, outline->max_contours,
+                          new_contours, AH_Point* ) )
+        goto Exit;
+
+      outline->max_contours = new_contours;
+    }
+
+    /* then, realloc the points, segments & edges arrays if needed */
+    if ( num_points > outline->max_points )
+    {
+      FT_Int  news = (num_points+7) & -8;
+      FT_Int  max  = outline->max_points;
+
+      if ( REALLOC_ARRAY( outline->points, max, news, AH_Point )          ||
+           REALLOC_ARRAY( outline->horz_edges,  max, news, AH_Edge )      ||
+           REALLOC_ARRAY( outline->horz_segments, max, news, AH_Segment ) )
+        goto Exit;
+
+      /* readjust some pointers */
+      outline->vert_edges    = outline->horz_edges + (news >> 1);
+      outline->vert_segments = outline->horz_segments + (news >> 1);
+      outline->max_points    = news;
+    }
+
+    outline->num_points   = num_points;
+    outline->num_contours = num_contours;
+
+    outline->num_hedges    = 0;
+    outline->num_vedges    = 0;
+    outline->num_hsegments = 0;
+    outline->num_vsegments = 0;
+
+    /* compute the vertical and horizontal major directions, this is     */
+    /* currently done by inspecting the 'ft_outline_reverse_fill' flag   */
+    /* However, some fonts have improper glyphs, and it'd be a good idea */
+    /* to be able to re-compute these values on the fly..                */
+    {
+      outline->vert_major_dir = ah_dir_up;
+      outline->horz_major_dir = ah_dir_left;
+
+      if (source->flags & ft_outline_reverse_fill)
+      {
+        outline->vert_major_dir = ah_dir_down;
+        outline->horz_major_dir = ah_dir_right;
+      }
+    }
+
+    outline->x_scale = face->size->metrics.x_scale;
+    outline->y_scale = face->size->metrics.y_scale;
+
+    points = outline->points;
+
+    {
+      /* do one thing at a time - it is easier to understand, and */
+      /* the code is clearer..                                    */
+      AH_Point*  point = points;
+      AH_Point*  limit = point + outline->num_points;
+
+      /* compute coordinates */
+      {
+        FT_Vector*  vec     = source->points;
+        FT_Fixed    x_scale = outline->x_scale;
+        FT_Fixed    y_scale = outline->y_scale;
+
+        for (; point < limit; vec++, point++ )
+        {
+          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 );
+          point->flags = 0;
+        }
+      }
+
+      /* compute bezier flags */
+      {
+        char*  tag = source->tags;
+        for ( point = points; point < limit; point++, tag++ )
+        {
+          switch ( FT_CURVE_TAG( *tag ) )
+          {
+            case FT_Curve_Tag_Conic: point->flags = ah_flah_conic; break;
+            case FT_Curve_Tag_Cubic: point->flags = ah_flah_cubic; break;
+            default:
+              ;
+          }
+        }
+      }
+
+      /* compute "next" and "prev" */
+      {
+        FT_Int    contour_index;
+        AH_Point* prev;
+        AH_Point* first;
+        AH_Point* end;
+
+        contour_index = 0;
+
+        first = points;
+        end   = points + source->contours[0];
+        prev  = end;
+
+        for ( point = points; point < limit; point++ )
+        {
+          point->prev = prev;
+          if (point < end)
+          {
+            point->next = point+1;
+            prev        = point;
+          }
+          else
+          {
+            point->next = first;
+            contour_index++;
+	    if (point+1 < limit)
+	    {
+              end   = points + source->contours[contour_index];
+              first = point+1;
+              prev  = end;
+	    }
+          }
+        }
+      }
+
+      /* set-up the contours array */
+      {
+        AH_Point** contour  = outline->contours;
+        AH_Point** limit    = contour + outline->num_contours;
+        short*     end      = source->contours;
+        short      index    = 0;
+
+        for (; contour < limit; contour++, end++ )
+        {
+          contour[0] = points + index;
+          index      = end[0] + 1;
+        }
+      }
+
+      /* compute directions of in & out vectors */
+      {
+        for ( point = points; point < limit; point++ )
+        {
+          AH_Point*  prev;
+          AH_Point*  next;
+          FT_Vector  vec;
+
+          prev  = point->prev;
+          vec.x = point->fx - prev->fx;
+          vec.y = point->fy - prev->fy;
+          point->in_dir   = ah_compute_direction( vec.x, vec.y );
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+          point->in_angle = ah_angle( &vec );
+#endif
+          next  = point->next;
+          vec.x = next->fx - point->fx;
+          vec.y = next->fy - point->fy;
+          point->out_dir   = ah_compute_direction( vec.x, vec.y );
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+          point->out_angle = ah_angle( &vec );
+
+          {
+            AH_Angle  delta = point->in_angle - point->out_angle;
+            if (delta < 0) delta = -delta;
+
+            if (delta < 2)
+              point->flags |= ah_flah_weak_interpolation;
+          }
+
+          /*
+          if (point->flags & (ah_flah_conic|ah_flah_cubic))
+            point->flags |= ah_flah_weak_interpolation;
+          */
+#endif
+
+#ifdef AH_OPTION_NO_STRONG_INTERPOLATION
+          point->flags |= ah_flah_weak_interpolation;
+#endif
+        }
+      }
+    }
+  Exit:
+    return error;
+  }
+
+
+
+  LOCAL_FUNC
+  void  ah_setup_uv( AH_Outline*  outline,
+                     AH_UV        source )
+  {
+    AH_Point*  point = outline->points;
+    AH_Point*  limit = point + outline->num_points;
+
+    for ( ; point < limit; point++ )
+    {
+      FT_Pos  u, v;
+
+      switch (source)
+      {
+        case ah_uv_fxy:  u = point->fx; v = point->fy; break;
+        case ah_uv_fyx:  u = point->fy; v = point->fx; break;
+        case ah_uv_oxy:  u = point->ox; v = point->oy; break;
+        case ah_uv_oyx:  u = point->oy; v = point->ox; break;
+        case ah_uv_yx:   u = point->y;  v = point->x;  break;
+        case ah_uv_ox:   u = point->x;  v = point->ox; break;
+        case ah_uv_oy:   u = point->y;  v = point->oy; break;
+        default: u = point->x;  v = point->y;  break;
+      }
+      point->u = u;
+      point->v = v;
+    }
+  }
+
+
+  LOCAL_FUNC
+  void  ah_outline_compute_segments( AH_Outline*  outline )
+  {
+    int           dimension;
+    AH_Segment*   segments;
+    FT_Int*       p_num_segments;
+    AH_Direction  segment_dir;
+    AH_Direction  major_dir;
+
+    segments       = outline->horz_segments;
+    p_num_segments = &outline->num_hsegments;
+    major_dir      = ah_dir_right;  /* !!! This value must be positive */
+    segment_dir    = major_dir;
+
+    /* set up (u,v) in each point */
+    ah_setup_uv( outline, ah_uv_fyx );
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Point**  contour       = outline->contours;
+      AH_Point**  contour_limit = contour + outline->num_contours;
+      AH_Segment* segment       = segments;
+      FT_Int      num_segments  = 0;
+
+#ifdef AH_HINT_METRICS
+      AH_Point*  min_point = 0;
+      AH_Point*  max_point = 0;
+      FT_Pos     min_coord = 32000;
+      FT_Pos     max_coord = -32000;
+#endif
+      /* do each contour separately */
+      for ( ; contour < contour_limit; contour++ )
+      {
+        AH_Point*  point   = contour[0];
+        AH_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 AH_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're 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) & ah_flah_control )
+                segment->flags |= ah_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're 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 */
+            memset( segment, 0, sizeof(*segment) );
+
+            segment->dir      = segment_dir;
+            segment->flags    = ah_edge_normal;
+            min_pos = max_pos = point->u;
+            segment->first    = point;
+            segment->last     = point;
+            segment->contour  = contour;
+            on_edge           = 1;
+
+            if (point == max_point)
+              max_point = 0;
+
+            if (point == min_point)
+              min_point = 0;
+          }
+
+          point = point->next;
+        }
+
+      } /* contours */
+
+
+#ifdef AH_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 (dimension == 0)
+      {
+        AH_Point*  point = outline->points;
+        AH_Point*  limit = point + outline->num_points;
+
+        AH_Point*  min_point = 0;
+        AH_Point*  max_point = 0;
+        FT_Pos     min_pos = 32000;
+        FT_Pos     max_pos = -32000;
+
+        /* compute minimum and maximum points */
+        for ( ; 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 */
+          memset( segment, 0, sizeof(*segment) );
+
+          segment->dir   = segment_dir;
+          segment->flags = ah_edge_normal;
+          segment->first = min_point;
+          segment->last  = min_point;
+          segment->pos   = min_pos;
+
+          num_segments++;
+          segment++;
+        }
+
+        /* insert maximum segment */
+        if (max_point)
+        {
+          /* clear all segment fields */
+          memset( segment, 0, sizeof(*segment) );
+
+          segment->dir   = segment_dir;
+          segment->flags = ah_edge_normal;
+          segment->first = max_point;
+          segment->last  = max_point;
+          segment->pos   = max_pos;
+
+          num_segments++;
+          segment++;
+        }
+      }
+#endif
+
+
+      *p_num_segments = num_segments;
+
+      segments       = outline->vert_segments;
+      major_dir      = ah_dir_up;
+      p_num_segments = &outline->num_vsegments;
+      ah_setup_uv( outline, ah_uv_fxy );
+    }
+  }
+
+
+
+  LOCAL_FUNC
+  void  ah_outline_link_segments( AH_Outline*  outline )
+  {
+    AH_Segment*  segments;
+    AH_Segment*  limit;
+    int          dimension;
+
+    ah_setup_uv( outline, ah_uv_fyx );
+
+    segments = outline->horz_segments;
+    limit    = segments + outline->num_hsegments;
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Segment*  seg1;
+      AH_Segment*  seg2;
+
+      /* now compare each segment to the others */
+      for ( seg1 = segments; seg1 < limit; seg1++ )
+      {
+        FT_Pos       best_score   = 32000;
+        AH_Segment*  best_segment = 0;
+
+        /* the fake segments are introduced to hint the metrics */
+        /* we must never link them to anything..                */
+        if (seg1->first == seg1->last)
+          continue;
+
+        for ( seg2 = segments; seg2 < limit; seg2++ )
+          if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 )
+          {
+            FT_Pos     pos1 = seg1->pos;
+            FT_Pos     pos2 = seg2->pos;
+            FT_Bool    is_dir;
+            FT_Bool    is_pos;
+
+            /* check that the segments are correctly oriented and positioned */
+            /* to form a black distance..                                    */
+
+            is_dir = ( seg1->dir == outline->horz_major_dir ||
+                       seg1->dir == outline->vert_major_dir );
+            is_pos = pos1 > pos2;
+
+            if ( pos1 == pos2 || !(is_dir ^ is_pos) )
+              continue;
+
+            /* check the two segments, we now have a better algorithm */
+            /* that doesn't rely on the segment points themselves but */
+            /* on their relative position. This gets rids of many     */
+            /* unpleasant artefacts and incorrect stem/serifs         */
+            /* computations..                                         */
+
+            /* first of all, compute the size of the "common" height */
+            {
+              FT_Pos  min = seg1->min_coord;
+              FT_Pos  max = seg1->max_coord;
+              FT_Pos  len, score;
+              FT_Pos  size1, size2;
+
+              size1 = max - min;
+              size2 = seg2->max_coord - seg2->min_coord;
+
+              if ( min < seg2->min_coord )
+                min = seg2->min_coord;
+
+              if ( max < seg2->max_coord )
+                max = seg2->max_coord;
+
+              len   = max - min;
+              score = seg2->pos - seg1->pos;
+              if (score < 0)
+                score = -score;
+
+              /* before comparing the scores, take care that the segments */
+              /* are really facing each other (often not for italics..)   */
+              if ( 4*len >= size1 && 4*len >= size2 )
+                if (score < best_score)
+                {
+                  best_score   = score;
+                  best_segment = seg2;
+                }
+            }
+          }
+
+        if (best_segment)
+        {
+          seg1->link  = best_segment;
+          seg1->score = best_score;
+          best_segment->num_linked++;
+        }
+
+
+      } /* edges 1 */
+
+      /* now, compute the "serif" segments */
+      for ( seg1 = segments; seg1 < limit; seg1++ )
+      {
+        seg2 = seg1->link;
+        if (seg2 && seg2->link != seg1)
+        {
+          seg1->link  = 0;
+          seg1->serif = seg2->link;
+        }
+      }
+
+      ah_setup_uv( outline, ah_uv_fxy );
+
+      segments = outline->vert_segments;
+      limit    = segments + outline->num_vsegments;
+    }
+  }
+
+
+#ifdef AH_DEBUG_GLYPH
+  /* A function used to dump the array of linked segments */
+  extern
+  void  ah_dump_segments( AH_Outline*  outline )
+  {
+    AH_Segment*  segments;
+    AH_Segment*  limit;
+    AH_Point*    points;
+    FT_Int       dimension;
+
+    points   = outline->points;
+    segments = outline->horz_segments;
+    limit    = segments + outline->num_hsegments;
+
+    for (dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_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 < limit; seg++ )
+      {
+        printf ( "  [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n",
+                 seg - segments,
+                 (int)seg->pos,
+                 seg->dir == ah_dir_up ? "up" :
+                 (seg->dir == ah_dir_down ? "down" :
+                  (seg->dir == ah_dir_left ? "left" :
+                   (seg->dir == ah_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 = outline->vert_segments;
+      limit    = segments + outline->num_vsegments;
+    }
+  }
+#endif
+
+
+
+  static
+  void ah_outline_compute_edges( AH_Outline*  outline )
+  {
+    AH_Edge*     edges;
+    AH_Segment*  segments;
+    AH_Segment*  segment_limit;
+    AH_Direction up_dir;
+    FT_Int*      p_num_edges;
+    FT_Int       dimension;
+    FT_Fixed     scale;
+    FT_Pos       edge_distance_threshold;
+
+    edges         = outline->horz_edges;
+    segments      = outline->horz_segments;
+    segment_limit = segments + outline->num_hsegments;
+    p_num_edges   = &outline->num_hedges;
+    up_dir        = ah_dir_right;
+    scale         = outline->y_scale;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Edge*     edge;
+      AH_Edge*     edge_limit;  /* really == edge + num_edges */
+      AH_Segment*  seg;
+
+      /**********************************************************************/
+      /*                                                                    */
+      /* 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_limit = edges;
+      for ( seg = segments; seg < segment_limit; seg++ )
+      {
+        AH_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;
+
+          dist = FT_MulFix( dist, scale );
+          if ( dist < edge_distance_threshold )
+          {
+            found = edge;
+            break;
+          }
+        }
+
+        if (!found)
+        {
+          /* insert a new edge in the list. Sort according to the position */
+          while ( edge > edges && edge[-1].fpos > seg->pos )
+          {
+            edge[0] = edge[-1];
+            edge--;
+          }
+          edge_limit++;
+
+          /* clear all edge fields */
+          memset( edge, 0, 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 = 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, straigth 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..                    */
+      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++ )
+      {
+        int  is_round    = 0;  /* does it contain round segments ?    */
+        int  is_straight = 0;  /* does it contain straight segments ? */
+        int  ups         = 0;  /* number of upwards segments          */
+        int  downs       = 0;  /* number of downwards segments        */
+
+        seg = edge->first;
+        do
+        {
+          FT_Bool is_serif;
+
+          /* check for roundness of segment */
+          if ( seg->flags & ah_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 = seg->serif && seg->serif->edge != edge;
+
+          if ( seg->link || is_serif )
+          {
+            AH_Edge*     edge2;
+            AH_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;
+
+            if (is_serif)
+              edge->serif = edge2;
+            else
+              edge->link  = edge2;
+          }
+
+          seg = seg->edge_next;
+
+        } while ( seg != edge->first );
+
+        /* set the round/straight flags */
+        edge->flags = ah_edge_normal;
+
+        if ( is_straight == 0 && is_round )
+          edge->flags |= ah_edge_round;
+
+        /* set the edge's main direction */
+        edge->dir = ah_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                    */
+        /* ZZZZ: this gets rid of many unpleasant artefacts !!  */
+        /*       example : the "c" in cour.pfa at size 13       */
+
+        if (edge->serif && edge->link)
+          edge->serif = 0;
+      }
+
+      edges         = outline->vert_edges;
+      segments      = outline->vert_segments;
+      segment_limit = segments + outline->num_vsegments;
+      p_num_edges   = &outline->num_vedges;
+      up_dir        = ah_dir_up;
+      scale         = outline->x_scale;
+    }
+  }
+
+
+ /************************************************************************
+  *
+  * <Function>
+  *    ah_outline_detect_features
+  *
+  * <Description>
+  *    Performs feature detection on a given AH_Outline
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  void  ah_outline_detect_features( AH_Outline*  outline )
+  {
+    ah_outline_compute_segments( outline );
+    ah_outline_link_segments   ( outline );
+    ah_outline_compute_edges   ( outline );
+  }
+
+
+
+ /************************************************************************
+  *
+  * <Function>
+  *    ah_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)
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  void  ah_outline_compute_blue_edges( AH_Outline*       outline,
+                                       AH_Face_Globals*  face_globals )
+  {
+    AH_Edge*     edge    = outline->horz_edges;
+    AH_Edge*     limit   = edge + outline->num_hedges;
+    AH_Globals*  globals = &face_globals->design;
+    FT_Fixed     y_scale = outline->y_scale;
+
+    /* compute for each horizontal edge, which blue zone is closer */
+    for ( ; edge < limit; edge++ )
+    {
+      AH_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 );
+      if (best_dist > 64/4)
+        best_dist = 64/4;
+
+      for ( blue = (AH_Blue)0; blue < ah_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 .. XXXX                         */
+        FT_Bool  is_top_blue  = AH_IS_TOP_BLUE(blue);
+        FT_Bool  is_major_dir = edge->dir == outline->horz_major_dir;
+
+        /* if it's a top zone, the edge must be against the major direction */
+        /* if it's 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 when the edge is over the reference position of a top zone,  */
+          /* or under the reference position of a bottom zone                 */
+          if ( edge->flags & ah_edge_round && dist != 0 )
+          {
+            FT_Bool  is_under_ref = 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>
+  *    ah_outline_scale_blue_edges
+  *
+  * <Description>
+  *    This functions must be called before hinting in order to re-adjust
+  *    the content of the detected edges (basically change the "blue edge"
+  *    pointer from 'design units' to 'scaled ones'
+  *
+  ************************************************************************/
+
+  LOCAL_FUNC
+  void  ah_outline_scale_blue_edges( AH_Outline*       outline,
+                                     AH_Face_Globals*  globals )
+  {
+    AH_Edge*          edge    = outline->horz_edges;
+    AH_Edge*          limit   = edge + outline->num_hedges;
+    FT_Int            delta;
+
+    delta = globals->scaled.blue_refs - globals->design.blue_refs;
+    for ( ; edge < limit; edge++ )
+    {
+      if (edge->blue_edge)
+        edge->blue_edge += delta;
+    }
+  }
+
+
+
+
+
+#ifdef AH_DEBUG_GLYPH
+  extern
+  void ah_dump_edges( AH_Outline*  outline )
+  {
+    AH_Edge*     edges;
+    AH_Edge*     limit;
+    AH_Segment*  segments;
+    FT_Int       dimension;
+
+    edges    = outline->horz_edges;
+    limit    = edges + outline->num_hedges;
+    segments = outline->horz_segments;
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_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 < limit; edge++ )
+      {
+        printf ( "  [ %5d | %4d | %5s | %4d | %5d |  %c  | %5.2f | %5.2f ]\n",
+                 edge - edges,
+                 (int)edge->fpos,
+                 edge->dir == ah_dir_up ? "up" :
+                 (edge->dir == ah_dir_down ? "down" :
+                  (edge->dir == ah_dir_left ? "left" :
+                   (edge->dir == ah_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 = outline->vert_edges;
+      limit = edges + outline->num_vedges;
+      segments = outline->vert_segments;
+    }
+  }
+#endif
--- /dev/null
+++ b/src/autohint/ahglyph.h
@@ -1,0 +1,79 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahglyph.h                                                              */
+/*                                                                         */
+/*    routines used to load and analyze a given glyph before hinting       */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifndef AGGLYPH_H
+#define AGGLYPH_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+  typedef enum AH_UV_
+  {
+    ah_uv_fxy,
+    ah_uv_fyx,
+    ah_uv_oxy,
+    ah_uv_oyx,
+    ah_uv_ox,
+    ah_uv_oy,
+    ah_uv_yx,
+    ah_uv_xy  /* should always be last !! */
+
+  } AH_UV;
+
+  LOCAL_DEF
+  void  ah_setup_uv( AH_Outline*  outline,
+                     AH_UV        source );
+
+
+  /* AH_Outline functions - they should be typically called in this order */
+
+  LOCAL_DEF
+  FT_Error  ah_outline_new( FT_Memory  memory, AH_Outline*  *aoutline );
+
+  LOCAL_DEF
+  FT_Error  ah_outline_load( AH_Outline*  outline, FT_Face  face );
+
+  LOCAL_DEF
+  void      ah_outline_compute_segments( AH_Outline*  outline );
+
+  LOCAL_DEF
+  void      ah_outline_link_segments( AH_Outline*  outline );
+
+  LOCAL_DEF
+  void      ah_outline_detect_features( AH_Outline*  outline );
+
+  LOCAL_DEF
+  void      ah_outline_compute_blue_edges( AH_Outline*       outline,
+                                           AH_Face_Globals*  globals );
+
+  LOCAL_DEF
+  void      ah_outline_scale_blue_edges( AH_Outline*       outline,
+                                         AH_Face_Globals*  globals );
+
+  LOCAL_DEF
+  void      ah_outline_save( AH_Outline*  outline, AH_Loader*  loader );
+
+  LOCAL_DEF
+  void      ah_outline_done( AH_Outline*  outline );
+
+
+#endif /* AGGLYPH_H */
--- /dev/null
+++ b/src/autohint/ahhint.c
@@ -1,0 +1,1293 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahhint.c                                                               */
+/*                                                                         */
+/*    Glyph hinter                                                         */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+
+#ifdef FT_FLAT_COMPILE
+#include "ahhint.h"
+#include "ahglyph.h"
+#include "ahangles.h"
+#else
+#include <autohint/ahhint.h>
+#include <autohint/ahglyph.h>
+#include <autohint/ahangles.h>
+#endif
+
+#include <freetype/ftoutln.h>
+
+#define FACE_GLOBALS(face)  ((AH_Face_Globals*)(face)->autohint.data)
+
+#define AH_USE_IUP
+
+
+ /*******************************************************************/
+ /*******************************************************************/
+ /****                                                           ****/
+ /****   Hinting routines                                        ****/
+ /****                                                           ****/
+ /*******************************************************************/
+ /*******************************************************************/
+
+  static int  disable_horz_edges = 0;
+  static int  disable_vert_edges = 0;
+
+  /* snap a given width in scaled coordinates to one of the */
+  /* current standard widths..                              */
+  static
+  FT_Pos  ah_snap_width( FT_Pos*     widths,
+                         FT_Int      count,
+                         FT_Pos      width )
+  {
+    int     n;
+    FT_Pos  best      = 64+32+2;
+    FT_Pos  reference = width;
+
+    for ( n = 0; n < count; n++ )
+    {
+      FT_Pos  w;
+      FT_Pos  dist;
+
+      w = widths[n];
+      dist = width - w;
+      if (dist < 0) dist = -dist;
+      if (dist < best)
+      {
+        best      = dist;
+        reference = w;
+      }
+    }
+
+    if ( width >= reference )
+    {
+      width -= 0x21;
+      if ( width < reference )
+        width = reference;
+    }
+    else
+    {
+      width += 0x21;
+      if ( width > reference )
+        width = reference;
+    }
+
+    return width;
+  }
+
+
+
+ /* Align one stem edge relative to the previous stem edge */
+  static
+  void  ah_align_linked_edge( AH_Hinter*  hinter,
+                              AH_Edge*    base_edge,
+                              AH_Edge*    stem_edge,
+                              int         vertical )
+  {
+    FT_Pos       dist    = stem_edge->opos - base_edge->opos;
+    AH_Globals*  globals = &hinter->globals->scaled;
+    FT_Pos       sign    = 1;
+
+    if (dist < 0)
+    {
+      dist = -dist;
+      sign = -1;
+    }
+
+    if (vertical)
+    {
+      dist = ah_snap_width( globals->heights, globals->num_heights, dist );
+
+      /* in the case of vertical hinting, always round */
+      /* the stem heights to integer pixels..          */
+      if (dist >= 64)
+        dist = (dist+16) & -64;
+      else
+        dist = 64;
+    }
+    else
+    {
+      dist = ah_snap_width( globals->widths,  globals->num_widths, dist );
+
+      if (hinter->flags & ah_hinter_monochrome)
+      {
+        /* monochrome horizontal hinting: snap widths to integer pixels */
+        /* with a different threshold..                                 */
+        if (dist < 64)
+          dist = 64;
+        else
+          dist = (dist+32) & -64;
+      }
+      else
+      {
+        /* for horizontal anti-aliased hinting, we adopt a more subtle */
+        /* approach, we strengthen small stems, round stems whose size */
+        /* is between 1 and 2 pixels to an integer, otherwise nothing  */
+        if (dist < 48)
+          dist = (dist+64) >> 1;
+
+        else if (dist < 128)
+          dist = (dist+42) & -64;
+
+      }
+    }
+    stem_edge->pos = base_edge->pos + sign*dist;
+  }
+
+
+
+  static
+  void  ah_align_serif_edge( AH_Hinter*  hinter,
+                             AH_Edge*    base,
+                             AH_Edge*    serif )
+  {
+    FT_Pos  dist;
+    FT_Pos  sign = 1;
+
+    UNUSED(hinter);
+    dist = serif->opos - base->opos;
+    if (dist < 0)
+    {
+      dist = -dist;
+      sign = -1;
+    }
+
+    if (base->flags & ah_edge_done)
+    /* do not strengthen serifs */
+    {
+      if (dist > 64)
+        dist = (dist+16) & -64;
+
+      else if (dist <= 32)
+        dist = (dist+33) >> 1;
+    }
+    serif->pos = base->pos + sign*dist;
+  }
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+ /****                                                                  ****/
+ /****       E D G E   H I N T I N G                                    ****/
+ /****                                                                  ****/
+ /****                                                                  ****/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+
+  /* Another alternative edge hinting algorithm */
+  static
+  void  ah_hint_edges_3( AH_Hinter*  hinter )
+  {
+    AH_Edge*      edges;
+    AH_Edge*      edge_limit;
+    AH_Outline*   outline = hinter->glyph;
+    FT_Int        dimension;
+
+    edges      = outline->horz_edges;
+    edge_limit = edges + outline->num_hedges;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Edge*  edge;
+      AH_Edge*  before = 0;
+      AH_Edge*  after  = 0;
+      AH_Edge*  anchor = 0;
+      int       has_serifs = 0;
+
+      if (disable_vert_edges && !dimension)
+        goto Next_Dimension;
+
+      if (disable_horz_edges && dimension)
+        goto Next_Dimension;
+
+      /* we begin by aligning all stems relative to the blue zone */
+      /* if needed.. that's only for horizontal edges..           */
+      if (dimension)
+      {
+        for ( edge = edges; edge < edge_limit; edge++ )
+        {
+          FT_Pos*  blue;
+          AH_Edge *edge1, *edge2;
+
+          if (edge->flags & ah_edge_done)
+            continue;
+
+          blue  = edge->blue_edge;
+          edge1 = 0;
+          edge2 = edge->link;
+
+          if (blue)
+          {
+            edge1 = edge;
+          }
+          else if (edge2 && edge2->blue_edge)
+          {
+            blue  = edge2->blue_edge;
+            edge1 = edge2;
+            edge2 = edge;
+          }
+
+          if (!edge1)
+            continue;
+
+          edge1->pos    = blue[0];
+          edge1->flags |= ah_edge_done;
+
+          if (edge2 && !edge2->blue_edge)
+          {
+            ah_align_linked_edge( hinter, edge1, edge2, dimension );
+            edge2->flags |= ah_edge_done;
+          }
+
+          if (!anchor)
+            anchor = edge;
+         }
+       }
+
+      /* now, we will align all stem edges, trying to maintain the */
+      /* relative order of stems in the glyph..                    */
+      before = 0;
+      after  = 0;
+      for ( edge = edges; edge < edge_limit; edge++ )
+      {
+        AH_Edge  *edge2;
+
+        if (edge->flags & ah_edge_done)
+          continue;
+
+        /* skip all non-stem edges */
+        edge2 = edge->link;
+        if (!edge2)
+        {
+          has_serifs++;
+          continue;
+        }
+
+        /* now, align the stem */
+
+        /* this should not happen, but it's better to be safe.. */
+        if (edge2->blue_edge || edge2 < edge)
+        {
+#if 0
+          printf( "strange blue alignement, edge %d to %d\n",
+                      edge - edges, edge2 - edges );
+#endif
+          ah_align_linked_edge( hinter, edge2, edge, dimension );
+          edge->flags |= ah_edge_done;
+          continue;
+        }
+
+        {
+          FT_Bool  min = 0;
+          FT_Pos   delta;
+
+          if (!anchor)
+          {
+            edge->pos = (edge->opos+32) & -64;
+            anchor    = edge;
+          }
+          else
+            edge->pos = anchor->pos + ((edge->opos - anchor->opos + 32) & -64);
+
+          edge->flags |= ah_edge_done;
+
+          if (edge > edges && edge->pos < edge[-1].pos)
+          {
+            edge->pos = edge[-1].pos;
+            min = 1;
+          }
+
+          ah_align_linked_edge( hinter, edge, edge2, dimension );
+          delta = 0;
+          if ( edge2+1 < edge_limit          &&
+               edge2[1].flags & ah_edge_done )
+            delta = edge2[1].pos - edge2->pos;
+
+          if (delta < 0)
+          {
+            edge2->pos += delta;
+            if (!min)
+              edge->pos += delta;
+          }
+          edge2->flags |= ah_edge_done;
+        }
+      }
+
+      if (!has_serifs)
+        goto Next_Dimension;
+
+      /* now, hint the remaining edges (serifs and single) in order */
+      /* to complete our processing..                               */
+      for ( edge = edges; edge < edge_limit; edge++ )
+      {
+        if (edge->flags & ah_edge_done)
+          continue;
+
+        if (edge->serif)
+        {
+          ah_align_serif_edge( hinter, edge->serif, edge );
+        }
+        else if (!anchor)
+        {
+          edge->pos = (edge->opos+32) & -64;
+          anchor    = edge;
+        }
+        else
+          edge->pos = anchor->pos + ((edge->opos-anchor->opos+32) & -64);
+
+        edge->flags |= ah_edge_done;
+
+        if (edge > edges && edge->pos < edge[-1].pos)
+          edge->pos = edge[-1].pos;
+
+        if ( edge+1 < edge_limit && edge[1].flags & ah_edge_done &&
+             edge->pos > edge[1].pos)
+          edge->pos = edge[1].pos;
+      }
+
+    Next_Dimension:
+      edges      = outline->vert_edges;
+      edge_limit = edges + outline->num_vedges;
+    }
+
+  }
+
+
+
+
+
+
+
+  LOCAL_FUNC
+  void  ah_hinter_hint_edges( AH_Hinter*  hinter,
+                              int         no_horz_edges,
+                              int         no_vert_edges )
+  {
+    disable_horz_edges = no_horz_edges;
+    disable_vert_edges = no_vert_edges;
+
+    /* AH_Interpolate_Blue_Edges( hinter ); -- doesn't seem to help   */
+    /* reduce the problem of the disappearing eye in the "e" of Times */
+    /* also, creates some artifacts near the blue zones ??            */
+    {
+      ah_hint_edges_3( hinter );
+
+    /* outline optimiser removed temporarily */
+#if 0
+      if (hinter->flags & ah_hinter_optimize)
+      {
+        AH_Optimizer  opt;
+
+        if (!AH_Optimizer_Init( &opt, hinter->glyph, hinter->memory ))
+        {
+          AH_Optimizer_Compute( &opt );
+          AH_Optimizer_Done( &opt );
+        }
+      }
+#endif
+    }
+  }
+
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+ /****                                                                  ****/
+ /****       P O I N T   H I N T I N G                                  ****/
+ /****                                                                  ****/
+ /****                                                                  ****/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+
+  static
+  void  ah_hinter_align_edge_points( AH_Hinter*  hinter )
+  {
+    AH_Outline*  outline = hinter->glyph;
+    AH_Edge*     edges;
+    AH_Edge*     edge_limit;
+    FT_Int       dimension;
+
+    edges       = outline->horz_edges;
+    edge_limit  = edges + outline->num_hedges;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Edge*   edge;
+      AH_Edge*   before;
+      AH_Edge*   after;
+
+      before = 0;
+      after  = 0;
+
+      edge = edges;
+      for ( ; edge < edge_limit; edge++ )
+      {
+        /* move the points of each segment in each edge to the edge's position */
+        AH_Segment*  seg = edge->first;
+        do
+        {
+          AH_Point*  point = seg->first;
+          for (;;)
+          {
+            if (dimension)
+            {
+              point->y      = edge->pos;
+              point->flags |= ah_flah_touch_y;
+            }
+            else
+            {
+              point->x      = edge->pos;
+              point->flags |= ah_flah_touch_x;
+            }
+            if (point == seg->last)
+              break;
+
+            point = point->next;
+          }
+
+          seg = seg->edge_next;
+        }
+        while (seg != edge->first);
+      }
+      edges      = outline->vert_edges;
+      edge_limit = edges + outline->num_vedges;
+    }
+  }
+
+
+  /* hint the strong points - this is equivalent to the TrueType "IP" */
+  static
+  void  ah_hinter_align_strong_points( AH_Hinter*  hinter )
+  {
+    AH_Outline*  outline = hinter->glyph;
+    FT_Int       dimension;
+    AH_Edge*     edges;
+    AH_Edge*     edge_limit;
+    AH_Point*    points;
+    AH_Point*    point_limit;
+    AH_Flags     touch_flag;
+
+    points      = outline->points;
+    point_limit = points + outline->num_points;
+
+    edges       = outline->horz_edges;
+    edge_limit  = edges + outline->num_hedges;
+    touch_flag  = ah_flah_touch_y;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Point*  point;
+      AH_Edge*   edge;
+      AH_Edge*   before;
+      AH_Edge*   after;
+
+      before = 0;
+      after  = 0;
+
+      if ( edges < edge_limit )
+        for ( point = points; point < point_limit; point++ )
+        {
+          FT_Pos  u, ou, fu;  /* point position */
+          FT_Pos  delta;
+
+          if ( point->flags & touch_flag )
+            continue;
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+          /* if this point is candidate to weak interpolation, we'll    */
+          /* interpolate it after all strong points have been processed */
+          if ( point->flags & ah_flah_weak_interpolation )
+            continue;
+#endif
+
+          if (dimension) { u = point->fy; ou = point->oy; }
+                    else { u = point->fx; ou = point->ox; }
+
+          fu = u;
+
+          /* is the point before the first edge ? */
+          edge  = edges;
+          delta = edge->fpos - u;
+          if (delta >= 0)
+          {
+            u = edge->pos - (edge->opos - ou);
+            goto Store_Point;
+          }
+
+          /* is the point after the last edge ? */
+          edge  = edge_limit-1;
+          delta = u - edge->fpos;
+          if ( delta >= 0 )
+          {
+            u = edge->pos + (ou - edge->opos);
+            goto Store_Point;
+          }
+
+          /* otherwise, interpolate the point in between */
+          {
+            AH_Edge*  before = 0;
+            AH_Edge*  after  = 0;
+
+            for ( edge = edges; edge < edge_limit; edge++ )
+            {
+              if ( u == edge->fpos )
+              {
+                u = edge->pos;
+                goto Store_Point;
+              }
+              if ( u < edge->fpos )
+                break;
+              before = edge;
+            }
+
+            for ( edge = edge_limit-1; edge >= edges; edge-- )
+            {
+              if ( u == edge->fpos )
+              {
+                u = edge->pos;
+                goto Store_Point;
+              }
+              if ( u > edge->fpos )
+                break;
+              after = edge;
+            }
+
+            /* assert( before && after && before != after ) */
+            u = before->pos + FT_MulDiv( fu - before->fpos,
+                                         after->pos - before->pos,
+                                         after->fpos - before->fpos );
+          }
+        Store_Point:
+
+          /* save the point position */
+          if (dimension) point->y = u;
+                   else  point->x = u;
+
+          point->flags |= touch_flag;
+        }
+
+      edges      = outline->vert_edges;
+      edge_limit = edges + outline->num_vedges;
+      touch_flag = ah_flah_touch_x;
+    }
+  }
+
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+  static
+  void  ah_iup_shift( AH_Point*  p1,
+                      AH_Point*  p2,
+                      AH_Point*  ref )
+  {
+    AH_Point*  p;
+    FT_Pos     delta = ref->u - ref->v;
+
+    for ( p = p1; p < ref; p++ )
+      p->u = p->v + delta;
+
+    for ( p = ref+1; p <= p2; p++ )
+      p->u = p->v + delta;
+  }
+
+
+  static
+  void  ah_iup_interp( AH_Point*  p1,
+                       AH_Point*  p2,
+                       AH_Point*  ref1,
+                       AH_Point*  ref2 )
+  {
+    AH_Point*  p;
+    FT_Pos     u;
+    FT_Pos     v1 = ref1->v;
+    FT_Pos     v2 = ref2->v;
+    FT_Pos     d1 = ref1->u - v1;
+    FT_Pos     d2 = ref2->u - v2;
+
+    if (p1 > p2) return;
+
+    if (v1 == v2)
+    {
+      for ( p = p1; p <= p2; p++ )
+      {
+        FT_Pos  u = p->v;
+
+        if (u <= v1) u += d1;
+                else u += d2;
+        p->u = u;
+      }
+      return;
+    }
+
+    if ( v1 < v2 )
+    {
+      for ( p = p1; p <= p2; p++ )
+      {
+        u = p->v;
+
+        if (u <= v1)      u += d1;
+        else if (u >= v2) u += d2;
+        else              u = ref1->u+FT_MulDiv( u-v1, ref2->u-ref1->u, v2-v1 );
+
+        p->u = u;
+      }
+    }
+    else
+    {
+      for ( p = p1; p <= p2; p++ )
+      {
+        u = p->v;
+        if (u <= v2)      u += d2;
+        else if (u >= v1) u += d1;
+        else              u = ref1->u+FT_MulDiv( u-v1, ref2->u-ref1->u, v2-v1 );
+
+        p->u = u;
+      }
+    }
+  }
+
+  /* interpolate weak points - this is equivalent to the TrueType "IUP" */
+  static
+  void  ah_hinter_align_weak_points( AH_Hinter*  hinter )
+  {
+    AH_Outline*  outline = hinter->glyph;
+    FT_Int       dimension;
+    AH_Edge*     edges;
+    AH_Edge*     edge_limit;
+    AH_Point*    points;
+    AH_Point*    point_limit;
+    AH_Point**   contour_limit;
+    AH_Flags     touch_flag;
+
+    points      = outline->points;
+    point_limit = points + outline->num_points;
+
+    /* PASS 1 : Move segment points to edge positions */
+
+    edges      = outline->horz_edges;
+    edge_limit = edges + outline->num_hedges;
+    touch_flag = ah_flah_touch_y;
+
+    contour_limit = outline->contours + outline->num_contours;
+
+    ah_setup_uv( outline, ah_uv_oy );
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Point*  point;
+      AH_Point*  end_point;
+      AH_Point*  first_point;
+      AH_Point** contour;
+
+      point   = points;
+      contour = outline->contours;
+
+      for ( ; contour < contour_limit; contour++ )
+      {
+        point       = *contour;
+        end_point   = point->prev;
+        first_point = point;
+
+        while (point <= end_point && !(point->flags & touch_flag))
+          point++;
+
+        if (point <= end_point)
+        {
+          AH_Point*  first_touched = point;
+          AH_Point*  cur_touched   = point;
+
+          point++;
+          while ( point <= end_point )
+          {
+            if (point->flags & touch_flag)
+            {
+              /* we found two succesive touched points, we interpolate */
+              /* all contour points between them..                     */
+              ah_iup_interp( cur_touched+1, point-1,
+                             cur_touched, point );
+              cur_touched = point;
+            }
+            point++;
+          }
+
+          if (cur_touched == first_touched)
+          {
+            /* this is a special case: only one point was touched in the */
+            /* contour.. we thus simply shift the whole contour..        */
+            ah_iup_shift( first_point, end_point, cur_touched );
+          }
+          else
+          {
+            /* now interpolate after the last touched point to the end */
+            /* of the contour..                                        */
+            ah_iup_interp( cur_touched+1, end_point,
+                           cur_touched, first_touched );
+
+            /* if the first contour point isn't touched, interpolate   */
+            /* from the contour start to the first touched point       */
+            if (first_touched > points)
+              ah_iup_interp( first_point, first_touched-1,
+                             cur_touched, first_touched );
+          }
+        }
+      }
+
+      /* now save the interpolated values back to x/y */
+      if (dimension)
+      {
+        for ( point = points; point < point_limit; point++ )
+          point->y = point->u;
+
+        touch_flag = ah_flah_touch_x;
+        ah_setup_uv( outline, ah_uv_ox );
+      }
+      else
+      {
+        for ( point = points; point < point_limit; point++ )
+          point->x = point->u;
+
+        break;  /* exit loop */
+      }
+    }
+  }
+#endif
+
+  LOCAL_FUNC
+  void  ah_hinter_align_points( AH_Hinter*  hinter )
+  {
+    ah_hinter_align_edge_points( hinter );
+
+#ifndef AH_OPTION_NO_STRONG_INTERPOLATION
+    ah_hinter_align_strong_points( hinter );
+#endif
+
+#ifndef AH_OPTION_NO_WEAK_INTERPOLATION
+    ah_hinter_align_weak_points( hinter );
+#endif
+  }
+
+
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+ /****                                                                  ****/
+ /****       H I N T E R   O B J E C T   M E T H O D S                  ****/
+ /****                                                                  ****/
+ /****                                                                  ****/
+ /**************************************************************************/
+ /**************************************************************************/
+ /**************************************************************************/
+
+  /* scale and fit the global metrics */
+  static
+  void  ah_hinter_scale_globals( AH_Hinter*  hinter,
+                                 FT_Fixed    x_scale,
+                                 FT_Fixed    y_scale )
+  {
+    FT_Int            n;
+    AH_Face_Globals*  globals = hinter->globals;
+    AH_Globals*       design = &globals->design;
+    AH_Globals*       scaled = &globals->scaled;
+
+    /* copy content */
+    *scaled = *design;
+
+    /* scale the standard widths & heights */
+    for ( n = 0; n < design->num_widths; n++ )
+      scaled->widths[n] = FT_MulFix( design->widths[n], x_scale );
+
+    for ( n = 0; n < design->num_heights; n++ )
+      scaled->heights[n] = FT_MulFix( design->heights[n], y_scale );
+
+    /* scale the blue zones */
+    for ( n = 0; n < ah_blue_max; n++ )
+    {
+      FT_Pos  delta, delta2;
+
+      delta = design->blue_shoots[n] - design->blue_refs[n];
+      delta2 = delta; if (delta < 0) delta2 = -delta2;
+      delta2 = FT_MulFix( delta2, y_scale );
+
+      if (delta2 < 32)
+        delta2 = 0;
+      else if (delta2 < 64)
+        delta2 = 32 + (((delta2-32)+16) & -32);
+      else
+        delta2 = (delta2+32) & -64;
+
+      if (delta < 0) delta2 = -delta2;
+
+      scaled->blue_refs  [n] = (FT_MulFix(design->blue_refs[n],y_scale)+32) & -64;
+      scaled->blue_shoots[n] = scaled->blue_refs[n] + delta2;
+    }
+  }
+
+
+  static
+  void  ah_hinter_align( AH_Hinter*  hinter )
+  {
+    ah_hinter_align_edge_points( hinter );
+    ah_hinter_align_points( hinter );
+  }
+
+
+ /* finalise a hinter object */
+  void ah_hinter_done( AH_Hinter*  hinter )
+  {
+    if (hinter)
+    {
+      FT_Memory  memory = hinter->memory;
+
+      ah_loader_done( hinter->loader );
+      ah_outline_done( hinter->glyph );
+
+     /* note: the globals pointer is _not_ owned by the hinter */
+     /*       but by the current face object, we don't need to */
+     /*       release it..                                     */
+      hinter->globals = 0;
+      hinter->face    = 0;
+
+      FREE(hinter);
+    }
+  }
+
+
+ /* create a new empty hinter object */
+  FT_Error ah_hinter_new( FT_Library library, AH_Hinter*  *ahinter )
+  {
+    AH_Hinter*  hinter = 0;
+    FT_Memory   memory = library->memory;
+    FT_Error    error;
+
+    *ahinter = 0;
+
+    /* allocate object */
+    if (ALLOC( hinter, sizeof(*hinter) )) goto Exit;
+
+    hinter->memory = memory;
+    hinter->flags  = 0;
+
+    /* allocate outline and loader */
+    error = ah_outline_new( memory, &hinter->glyph )  ||
+            ah_loader_new ( memory, &hinter->loader ) ||
+            ah_loader_create_extra( hinter->loader );
+    if (error) goto Exit;
+
+    *ahinter = hinter;
+
+  Exit:
+    if (error)
+      ah_hinter_done( hinter );
+
+    return error;
+  }
+
+
+ /* create a face's autohint globals */
+  FT_Error  ah_hinter_new_face_globals( AH_Hinter*  hinter,
+                                        FT_Face     face,
+                                        AH_Globals* globals )
+  {
+    FT_Error          error;
+    FT_Memory         memory = hinter->memory;
+    AH_Face_Globals*  face_globals;
+
+    if ( ALLOC( face_globals, sizeof(*face_globals) ) )
+      goto Exit;
+
+    hinter->face    = face;
+    hinter->globals = face_globals;
+    if (globals)
+      face_globals->design = *globals;
+    else
+      ah_hinter_compute_globals( hinter );
+
+    face->autohint.data      = face_globals;
+    face->autohint.finalizer = (FT_Generic_Finalizer)ah_hinter_done_face_globals;
+    face_globals->face       = face;
+
+  Exit:
+    return error;
+  }
+
+
+
+ /* discard a face's autohint globals */
+  void  ah_hinter_done_face_globals( AH_Face_Globals*  globals )
+  {
+    FT_Face   face   = globals->face;
+    FT_Memory memory = face->memory;
+
+    FREE( globals );
+  }
+
+
+
+ static
+ FT_Error  ah_hinter_load( AH_Hinter*  hinter,
+                           FT_UInt     glyph_index,
+                           FT_UInt     load_flags,
+                           FT_UInt     depth )
+ {
+   FT_Face           face    = hinter->face;
+   FT_GlyphSlot      slot    = face->glyph;
+   FT_Fixed          x_scale = face->size->metrics.x_scale;
+   FT_Fixed          y_scale = face->size->metrics.y_scale;
+   FT_Glyph_Metrics  metrics;  /* temporary metrics */
+   FT_Error          error;
+   AH_Outline*       outline = hinter->glyph;
+   AH_Loader*        gloader = hinter->loader;
+   FT_Bool           no_horz_hints = (load_flags & AH_HINT_NO_HORZ_EDGES) != 0;
+   FT_Bool           no_vert_hints = (load_flags & AH_HINT_NO_VERT_EDGES) != 0;
+
+   /* load the glyph */
+   error = FT_Load_Glyph( face, glyph_index, load_flags );
+   if (error) goto Exit;
+
+   /* save current glyph metrics */
+   metrics = slot->metrics;
+
+   switch (slot->format)
+   {
+     case ft_glyph_format_outline:
+       {
+         /* first of all, copy the outline points in the loader's current  */
+         /* extra points, which is used to keep original glyph coordinates */
+         error = ah_loader_check_points( gloader, slot->outline.n_points+2,
+                                         slot->outline.n_contours );
+         if (error) goto Exit;
+
+         MEM_Copy( gloader->current.extra_points, slot->outline.points,
+                   slot->outline.n_points * sizeof(FT_Vector) );
+
+         MEM_Copy( gloader->current.outline.contours, slot->outline.contours,
+                   slot->outline.n_contours*sizeof(short) );
+
+         MEM_Copy( gloader->current.outline.tags, slot->outline.tags,
+                   slot->outline.n_points * sizeof(char) );
+
+         gloader->current.outline.n_points   = slot->outline.n_points;
+         gloader->current.outline.n_contours = slot->outline.n_contours;
+
+         /* compute original phantom points */
+         hinter->pp1.x = 0;
+         hinter->pp1.y = 0;
+         hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
+         hinter->pp2.y = 0;
+
+         /* be sure to check for spacing glyphs */
+         if (slot->outline.n_points == 0)
+           goto Hint_Metrics;
+
+         /* now, load the slot image into the auto-outline, and run the */
+         /* automatic hinting process..                                 */
+         error = ah_outline_load( outline, face );   /* XXXX: change to slot */
+         if (error) goto Exit;
+
+         /* perform feature detection */
+         ah_outline_detect_features( outline );
+
+         if ( !no_horz_hints )
+         {
+           ah_outline_compute_blue_edges( outline, hinter->globals );
+           ah_outline_scale_blue_edges( outline, hinter->globals );
+         }
+
+         /* perform alignment control */
+         ah_hinter_hint_edges( hinter, no_horz_hints, no_vert_hints );
+         ah_hinter_align( hinter );
+
+         /* now save the current outline into the loader's current table */
+         ah_outline_save( outline, gloader );
+
+         /* we now need to hint the metrics according to the change in */
+         /* width/positioning that occured during the hinting process  */
+         {
+           FT_Pos  old_width, new_width;
+           FT_Pos  old_advance, new_advance;
+           FT_Pos  old_lsb, new_lsb;
+           AH_Edge*  edge1   = hinter->glyph->horz_edges;            /* left-most edge  */
+           AH_Edge*  edge2   = edge1 + hinter->glyph->num_hedges-1;  /* right-mode edge */
+
+           old_width = edge2->opos - edge1->opos;
+           new_width = edge2->pos  - edge1->pos;
+
+           old_advance = hinter->pp2.x;
+           old_lsb     = edge1->opos;
+           new_lsb     = edge1->pos;
+
+           new_advance = old_advance + (new_width+new_lsb-old_width-old_lsb);
+
+           hinter->pp1.x = ((new_lsb - old_lsb)+32) & -64;
+           hinter->pp2.x = ((edge2->pos + (old_advance - edge2->opos))+32) & -64;
+         }
+
+         /* good, we simply add the glyph to our loader's base */
+         ah_loader_add( gloader );
+       }
+       break;
+
+     case ft_glyph_format_composite:
+       {
+         FT_UInt       nn, num_subglyphs = slot->num_subglyphs;
+         FT_UInt       num_base_subgs, start_point, start_contour;
+         FT_SubGlyph*  subglyph;
+
+         start_point   = gloader->base.outline.n_points;
+         start_contour = gloader->base.outline.n_contours;
+
+         /* first of all, copy the subglyph descriptors in the glyph loader */
+         error = ah_loader_check_subglyphs( gloader, num_subglyphs );
+         if (error) goto Exit;
+
+         MEM_Copy( gloader->current.subglyphs, slot->subglyphs,
+                   num_subglyphs*sizeof(FT_SubGlyph) );
+
+         gloader->current.num_subglyphs = num_subglyphs;
+         num_base_subgs = gloader->base.num_subglyphs;
+
+         /* now, read each subglyph independently */
+         for ( nn = 0; nn < num_subglyphs; nn++ )
+         {
+           FT_Vector  pp1, pp2;
+           FT_Pos     x, y;
+           FT_UInt    num_points, num_new_points, num_base_points;
+
+           /* gloader.current.subglyphs can change during glyph loading due */
+           /* to re-allocation. We must recompute the current subglyph on   */
+           /* each iteration..                                              */
+           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
+
+           pp1 = hinter->pp1;
+           pp2 = hinter->pp2;
+
+           num_base_points = gloader->base.outline.n_points;
+
+           error = ah_hinter_load( hinter, subglyph->index, load_flags, depth+1 );
+           if ( error ) goto Exit;
+
+           /* recompute subglyph pointer */
+           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
+
+           if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
+           {
+             pp1 = hinter->pp1;
+             pp2 = hinter->pp2;
+           }
+           else
+           {
+             hinter->pp1 = pp1;
+             hinter->pp2 = pp2;
+           }
+
+           num_points     = gloader->base.outline.n_points;
+           num_new_points = num_points - num_base_points;
+
+           /* now perform the transform required for this subglyph */
+
+           if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE    |
+                                    FT_SUBGLYPH_FLAG_XY_SCALE |
+                                    FT_SUBGLYPH_FLAG_2X2      ) )
+           {
+             FT_Vector*  cur   = gloader->base.outline.points + num_base_points;
+             FT_Vector*  org   = gloader->base.extra_points   + num_base_points;
+             FT_Vector*  limit = cur + num_new_points;
+
+             for ( ; cur < limit; cur++, org++ )
+             {
+               FT_Vector_Transform( cur, &subglyph->transform );
+               FT_Vector_Transform( org, &subglyph->transform );
+             }
+           }
+
+           /* apply offset */
+
+           if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
+           {
+             FT_Int      k = subglyph->arg1;
+             FT_UInt     l = subglyph->arg2;
+             FT_Vector*  p1;
+             FT_Vector*  p2;
+
+             if ( start_point + k >= num_base_points          ||
+                                l >= (FT_UInt)num_new_points  )
+             {
+               error = FT_Err_Invalid_Composite;
+               goto Exit;
+             }
+
+             l += num_base_points;
+
+             /* for now, only use the current point coordinates     */
+             /* we may consider another approach in the near future */
+             p1 = gloader->base.outline.points + start_point + k;
+             p2 = gloader->base.outline.points + start_point + l;
+
+             x = p1->x - p2->x;
+             y = p1->y - p2->y;
+           }
+           else
+           {
+             x = FT_MulFix( subglyph->arg1, x_scale );
+             y = FT_MulFix( subglyph->arg2, y_scale );
+
+             x = ( x + 32 ) & -64;
+             y = ( y + 32 ) & -64;
+           }
+
+           {
+             FT_Outline  dummy = gloader->base.outline;
+             dummy.points  += num_base_points;
+             dummy.n_points = num_new_points;
+
+             FT_Outline_Translate( &dummy, x, y );
+           }
+         }
+       }
+       break;
+
+     default:
+       /* we don't support other formats (yet ?) */
+       error = FT_Err_Unimplemented_Feature;
+   }
+
+ Hint_Metrics:
+   if (depth == 0)
+   {
+     FT_BBox  bbox;
+
+     /* we must translate our final outline by -pp1.x, and compute */
+     /* the new metrics..                                          */
+     if (hinter->pp1.x)
+       FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
+
+     FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
+     bbox.xMin &= -64;
+     bbox.yMin &= -64;
+     bbox.xMax  = (bbox.xMax+63) & -64;
+     bbox.yMax  = (bbox.yMax+63) & -64;
+
+     slot->metrics.width        = (bbox.xMax - bbox.xMin);
+     slot->metrics.height       = (bbox.yMax - bbox.yMin);
+     slot->metrics.horiBearingX = bbox.xMin;
+     slot->metrics.horiBearingY = bbox.yMax;
+     slot->metrics.horiAdvance  = hinter->pp2.x - hinter->pp1.x;
+     /* XXXX: TO DO - slot->linearHoriAdvance */
+
+     /* now copy outline into glyph slot */
+     ah_loader_rewind( slot->loader );
+     error = ah_loader_copy_points( slot->loader, gloader );
+     if (error) goto Exit;
+
+     slot->outline = slot->loader->base.outline;
+     slot->format  = ft_glyph_format_outline;
+   }
+
+ Exit:
+   return error;
+ }
+
+
+ /* load and hint a given glyph */
+ FT_Error  ah_hinter_load_glyph( AH_Hinter*    hinter,
+                                 FT_GlyphSlot  slot,
+                                 FT_Size       size,
+                                 FT_UInt       glyph_index,
+                                 FT_Int        load_flags )
+ {
+   FT_Face           face         = slot->face;
+   FT_Error          error;
+   FT_Fixed          x_scale      = size->metrics.x_scale;
+   FT_Fixed          y_scale      = size->metrics.y_scale;
+   AH_Face_Globals*  face_globals = FACE_GLOBALS(face);
+
+   /* first of all, we need to check that we're using the correct face and */
+   /* global hints to load the glyph                                       */
+   if ( hinter->face != face || hinter->globals != face_globals )
+   {
+     hinter->face = face;
+     if (!face_globals)
+     {
+       error = ah_hinter_new_face_globals( hinter, face, 0 );
+       if (error) goto Exit;
+     }
+     hinter->globals = FACE_GLOBALS(face);
+     face_globals    = FACE_GLOBALS(face);
+   }
+
+   /* now, we must check the current character pixel size to see if we need */
+   /* to rescale the global metrics..                                       */
+   if ( face_globals->x_scale != x_scale ||
+        face_globals->y_scale != y_scale )
+   {
+     ah_hinter_scale_globals( hinter, x_scale, y_scale );
+   }
+
+   load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE;
+
+   ah_loader_rewind( hinter->loader );
+
+   error = ah_hinter_load( hinter, glyph_index, load_flags, 0 );
+
+ Exit:
+   return error;
+ }
+
+
+ /* retrieve a face's autohint globals for client applications */
+  void  ah_hinter_get_global_hints( AH_Hinter*  hinter,
+                                    FT_Face     face,
+                                    void*      *global_hints,
+                                    long       *global_len )
+  {
+    AH_Globals*  globals = 0;
+    FT_Memory    memory  = hinter->memory;
+    FT_Error     error;
+
+    /* allocate new master globals */
+    if (ALLOC( globals, sizeof(*globals)))
+      goto Fail;
+
+    /* compute face globals if needed */
+    if (!FACE_GLOBALS(face))
+    {
+      error = ah_hinter_new_face_globals( hinter, face, 0 );
+      if (error) goto Fail;
+    }
+
+    *globals = FACE_GLOBALS(face)->design;
+    *global_hints = globals;
+    *global_len   = sizeof(*globals);
+    return;
+
+  Fail:
+    FREE( globals );
+    *global_hints = 0;
+    *global_len   = 0;
+  }
+
+
+  void  ah_hinter_done_global_hints( AH_Hinter*  hinter,
+                                     void*       global_hints )
+  {
+    FT_Memory memory = hinter->memory;
+    FREE( global_hints );
+  }
+
+
--- /dev/null
+++ b/src/autohint/ahhint.h
@@ -1,0 +1,65 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahhint.h                                                               */
+/*                                                                         */
+/*    Glyph hinter declarations                                            */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifndef AGHINT_H
+#define AGHINT_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahglobal.h"
+#else
+#include <autohint/ahglobal.h>
+#endif
+
+#define AH_HINT_DEFAULT         0
+#define AH_HINT_NO_ALIGNMENT    1
+#define AH_HINT_NO_HORZ_EDGES   0x20000
+#define AH_HINT_NO_VERT_EDGES   0x40000
+
+
+
+ /* create a new empty hinter object */
+  extern
+  FT_Error ah_hinter_new( FT_Library library, AH_Hinter*  *ahinter );
+
+ /* Load a hinted glyph in the hinter */
+  extern
+  FT_Error  ah_hinter_load_glyph( AH_Hinter*    hinter,
+                                  FT_GlyphSlot  slot,
+                                  FT_Size       size,
+                                  FT_UInt       glyph_index,
+                                  FT_Int        load_flags );
+
+ /* finalise a hinter object */
+  extern
+  void     ah_hinter_done( AH_Hinter*  hinter );
+
+  LOCAL_DEF
+  void     ah_hinter_done_face_globals( AH_Face_Globals*  globals );
+
+  extern
+  void     ah_hinter_get_global_hints( AH_Hinter*  hinter,
+                                       FT_Face     face,
+				       void*      *global_hints,
+				       long       *global_len );
+
+  extern
+  void     ah_hinter_done_global_hints( AH_Hinter*  hinter,
+                                        void*       global_hints );
+
+#endif /* AGHINT_H */
--- /dev/null
+++ b/src/autohint/ahloader.h
@@ -1,0 +1,103 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahloader.h                                                             */
+/*                                                                         */
+/*    Glyph loader implementation for the auto-hinting module              */
+/*    This defines the AG_GlyphLoader type in two different ways:          */
+/*                                                                         */
+/*      - when the module is compiled within FreeType 2, the type          */
+/*        is simply a typedef to FT_GlyphLoader                            */
+/*                                                                         */
+/*      - when the module is compiled as a standalone object,              */
+/*        AG_GlyphLoader has its own implementation..                      */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifndef AGLOADER_H
+#define AGLOADER_H
+
+#ifdef _STANDALONE_
+
+  typedef struct AH_GlyphLoad_
+  {
+    FT_Outline    outline;       /* outline                          */
+    FT_UInt       num_subglyphs; /* number of subglyphs              */
+    FT_SubGlyph*  subglyphs;     /* subglyphs                        */
+    FT_Vector*    extra_points;  /* extra points table..             */
+
+  } AH_GlyphLoad;
+
+
+  struct AH_GlyphLoader_
+  {
+    FT_Memory     memory;
+    FT_UInt       max_points;
+    FT_UInt       max_contours;
+    FT_UInt       max_subglyphs;
+    FT_Bool       use_extra;
+
+    AH_GlyphLoad  base;
+    AH_GlyphLoad  current;
+
+    void*         other;            /* for possible future extension ? */
+
+  };
+
+
+  LOCAL_DEF  FT_Error  AH_GlyphLoader_New( FT_Memory         memory,
+                                           AH_GlyphLoader*  *aloader );
+
+  LOCAL_DEF  FT_Error  AH_GlyphLoader_Create_Extra( AH_GlyphLoader*  loader );
+
+  LOCAL_DEF  void      AH_GlyphLoader_Done( AH_GlyphLoader*  loader );
+
+  LOCAL_DEF  void      AH_GlyphLoader_Reset( AH_GlyphLoader*  loader );
+
+  LOCAL_DEF  void      AH_GlyphLoader_Rewind( AH_GlyphLoader*  loader );
+
+  LOCAL_DEF  FT_Error  AH_GlyphLoader_Check_Points( AH_GlyphLoader* loader,
+                                                    FT_UInt         n_points,
+                                                    FT_UInt         n_contours );
+
+  LOCAL_DEF  FT_Error  AH_GlyphLoader_Check_Subglyphs( AH_GlyphLoader*  loader,
+                                                       FT_UInt          n_subs );
+
+  LOCAL_DEF  void      AH_GlyphLoader_Prepare( AH_GlyphLoader*  loader );
+
+
+  LOCAL_DEF  void      AH_GlyphLoader_Add( AH_GlyphLoader*  loader );
+
+  LOCAL_DEF  FT_Error  AH_GlyphLoader_Copy_Points( AH_GlyphLoader*  target,
+                                                   FT_GlyphLoader*  source );
+
+#else
+#include <freetype/internal/ftobjs.h>
+
+  #define AH_Load    FT_GlyphLoad
+  #define AH_Loader  FT_GlyphLoader
+
+  #define ah_loader_new              FT_GlyphLoader_New
+  #define ah_loader_done             FT_GlyphLoader_Done
+  #define ah_loader_reset            FT_GlyphLoader_Reset
+  #define ah_loader_rewind           FT_GlyphLoader_Rewind
+  #define ah_loader_create_extra     FT_GlyphLoader_Create_Extra
+  #define ah_loader_check_points     FT_GlyphLoader_Check_Points
+  #define ah_loader_check_subglyphs  FT_GlyphLoader_Check_Subglyphs
+  #define ah_loader_prepare          FT_GlyphLoader_Prepare
+  #define ah_loader_add              FT_GlyphLoader_Add
+  #define ah_loader_copy_points      FT_GlyphLoader_Copy_Points
+
+#endif
+
+#endif /* AGLOADER_H */
--- /dev/null
+++ b/src/autohint/ahmodule.c
@@ -1,0 +1,111 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahmodule.c                                                             */
+/*                                                                         */
+/*    Auto-hinting module implementation                                   */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#include <freetype/ftmodule.h>
+
+#ifdef FT_FLAT_COMPILE
+#include "ahhint.h"
+#else
+#include <autohint/ahhint.h>
+#endif
+
+
+  typedef struct FT_AutoHinterRec_
+  {
+    FT_ModuleRec   root;
+    AH_Hinter*     hinter;
+
+  } FT_AutoHinterRec;
+
+
+  static
+  FT_Error  ft_autohinter_init( FT_AutoHinter  module )
+  {
+    return ah_hinter_new( module->root.library, &module->hinter );
+  }
+
+  static
+  void      ft_autohinter_done( FT_AutoHinter  module )
+  {
+    ah_hinter_done( module->hinter );
+  }
+
+  static
+  FT_Error  ft_autohinter_load( FT_AutoHinter  module,
+                                FT_GlyphSlot   slot,
+				FT_Size        size,
+				FT_UInt        glyph_index,
+				FT_ULong       load_flags )
+  {
+    return ah_hinter_load_glyph( module->hinter,
+                                 slot, size, glyph_index, load_flags );
+  }
+
+  static
+  void   ft_autohinter_reset( FT_AutoHinter  module,
+                              FT_Face        face )
+  {
+    UNUSED(module);
+    if (face->autohint.data)
+      ah_hinter_done_face_globals( face->autohint.data );
+  }
+
+  static
+  void  ft_autohinter_get_globals( FT_AutoHinter  module,
+                                   FT_Face        face,
+				   void*         *global_hints,
+				   long          *global_len )
+  {
+    ah_hinter_get_global_hints( module->hinter, face,
+                                global_hints, global_len );
+  }
+
+
+  static
+  void  ft_autohinter_done_globals( FT_AutoHinter  module,
+                                    void*          global_hints )
+  {
+    ah_hinter_done_global_hints( module->hinter, global_hints );
+  }
+
+
+  static
+  const FT_AutoHinter_Interface  autohinter_interface =
+  {
+    ft_autohinter_reset,
+    ft_autohinter_load,
+    ft_autohinter_get_globals,
+    ft_autohinter_done_globals
+  };
+
+  const FT_Module_Class  autohint_module_class =
+  {
+    ft_module_hinter,
+    sizeof( FT_AutoHinterRec ),
+
+    "autohinter",
+    0x10000,   /* version 1.0 of the autohinter  */
+    0x20000,   /* requires FreeType 2.0 or above */
+
+    (const void*)&autohinter_interface,
+
+    (FT_Module_Constructor)  ft_autohinter_init,
+    (FT_Module_Destructor)   ft_autohinter_done,
+    (FT_Module_Requester)    0
+  };
--- /dev/null
+++ b/src/autohint/ahmodule.h
@@ -1,0 +1,27 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahmodule.h                                                             */
+/*                                                                         */
+/*    Auto-hinting module declaration                                      */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+#ifndef AHMODULE_H
+#define AHMODULE_H
+
+#include <freetype/ftmodule.h>
+
+  FT_EXPORT_VAR(const FT_Module_Class)  autohint_module_class;
+
+#endif /* AHMODULE_H */
--- /dev/null
+++ b/src/autohint/ahoptim.c
@@ -1,0 +1,808 @@
+/***************************************************************************/
+/*                                                                         */
+/*  FreeType Auto-Gridder Outline Optimisation                             */
+/*                                                                         */
+/*  This module is in charge of optimising the outlines produced by the    */
+/*  auto-hinter in direct mode. This is required at small pixel sizes in   */
+/*  order to ensure coherent spacing, among other things..                 */
+/*                                                                         */
+/*  The technique used in this module is a simplified simulated annealing. */
+/*                                                                         */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+
+#include <freetype/internal/ftobjs.h>  /* for ALLOC_ARRAY and FREE */
+
+#ifdef FT_FLAT_COMPILE
+#include "ahoptim.h"
+#else
+#include <autohint/ahoptim.h>
+#endif
+
+/* define this macro to use brute force optimisation, this is slow, but */
+/* a good way to perfect the distortion function "by hand" through      */
+/* tweaking..                                                           */
+#define BRUTE_FORCE
+
+#define xxxDEBUG_OPTIM
+
+#undef LOG
+#ifdef DEBUG_OPTIM
+#define LOG(x)  optim_log##x
+#else
+#define LOG(x)
+#endif
+
+#ifdef DEBUG_OPTIM
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define FLOAT(x)  ((float)((x)/64.0))
+
+static
+void optim_log( const char*  fmt, ... )
+{
+  va_list  ap;
+
+
+  va_start( ap, fmt );
+  vprintf( fmt, ap );
+  va_end( ap );
+}
+#endif
+
+
+#ifdef DEBUG_OPTIM
+  static
+  void  AH_Dump_Stems( AH_Optimizer*  optimizer )
+  {
+    int       n;
+    AH_Stem*  stem;
+
+    stem = optimizer->stems;
+    for ( n = 0; n < optimizer->num_stems; n++, stem++ )
+    {
+      LOG(( " %c%2d [%.1f:%.1f]={%.1f:%.1f}=<%1.f..%1.f> force=%.1f speed=%.1f\n",
+            optimizer->vertical ? 'V' : 'H', n,
+            FLOAT(stem->edge1->opos), FLOAT(stem->edge2->opos),
+            FLOAT(stem->edge1->pos),  FLOAT(stem->edge2->pos),
+            FLOAT(stem->min_pos),     FLOAT(stem->max_pos),
+            FLOAT(stem->force),       FLOAT(stem->velocity) ));
+    }
+  }
+
+  static
+  void  AH_Dump_Stems2( AH_Optimizer*  optimizer )
+  {
+    int       n;
+    AH_Stem*  stem;
+
+    stem = optimizer->stems;
+    for ( n = 0; n < optimizer->num_stems; n++, stem++ )
+    {
+      LOG(( " %c%2d [%.1f]=<%1.f..%1.f> force=%.1f speed=%.1f\n",
+            optimizer->vertical ? 'V' : 'H', n,
+            FLOAT(stem->pos),
+            FLOAT(stem->min_pos),     FLOAT(stem->max_pos),
+            FLOAT(stem->force),       FLOAT(stem->velocity) ));
+    }
+  }
+
+  static
+  void  AH_Dump_Springs( AH_Optimizer*  optimizer )
+  {
+    int  n;
+    AH_Spring*  spring;
+    AH_Stem*    stems;
+
+    spring = optimizer->springs;
+    stems  = optimizer->stems;
+    LOG(( "%cSprings ", optimizer->vertical ? 'V' : 'H' ));
+    for ( n = 0; n < optimizer->num_springs; n++, spring++ )
+    {
+      LOG(( " [%d-%d:%.1f:%1.f:%.1f]", spring->stem1 - stems, spring->stem2 - stems,
+            FLOAT(spring->owidth),
+            FLOAT(spring->stem2->pos-(spring->stem1->pos+spring->stem1->width)),
+            FLOAT(spring->tension) ));
+    }
+
+    LOG(( "\n" ));
+  }
+#endif
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****   COMPUTE STEMS AND SPRINGS IN AN OUTLINE                       ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  static
+  int  valid_stem_segments( AH_Segment*  seg1, AH_Segment*  seg2 )
+  {
+    return seg1->serif == 0 && seg2 && seg2->link == seg1 && seg1->pos < seg2->pos &&
+           seg1->min_coord <= seg2->max_coord &&
+           seg2->min_coord <= seg1->max_coord;
+  }
+
+  /* compute all stems in an outline */
+  static
+  int  optim_compute_stems( AH_Optimizer* optimizer )
+  {
+    AH_Outline*  outline = optimizer->outline;
+    FT_Fixed     scale;
+    FT_Memory    memory  = optimizer->memory;
+    FT_Error     error   = 0;
+    FT_Int       dimension;
+    AH_Edge*     edges;
+    AH_Edge*     edge_limit;
+    AH_Stem**    p_stems;
+    FT_Int*      p_num_stems;
+
+    edges      = outline->horz_edges;
+    edge_limit = edges + outline->num_hedges;
+    scale      = outline->y_scale;
+
+    p_stems     = &optimizer->horz_stems;
+    p_num_stems = &optimizer->num_hstems;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      AH_Stem*  stems     = 0;
+      FT_Int    num_stems = 0;
+      AH_Edge*  edge;
+
+      /* first of all, count the number of stems in this direction */
+      for ( edge = edges; edge < edge_limit; edge++ )
+      {
+        AH_Segment*  seg = edge->first;
+        do
+        {
+          if (valid_stem_segments( seg, seg->link ))
+            num_stems++;
+
+          seg = seg->edge_next;
+
+        } while (seg != edge->first);
+      }
+
+      /* now allocate the stems and build their table */
+      if (num_stems > 0)
+      {
+        AH_Stem*  stem;
+
+        if ( ALLOC_ARRAY( stems, num_stems, AH_Stem ) )
+          goto Exit;
+
+        stem = stems;
+        for ( edge = edges; edge < edge_limit; edge++ )
+        {
+          AH_Segment*  seg = edge->first;
+          AH_Segment*  seg2;
+          do
+          {
+            seg2 = seg->link;
+            if (valid_stem_segments(seg,seg2))
+            {
+              AH_Edge*  edge1 = seg->edge;
+              AH_Edge*  edge2 = seg2->edge;
+
+              stem->edge1  = edge1;
+              stem->edge2  = edge2;
+              stem->opos   = edge1->opos;
+              stem->pos    = edge1->pos;
+              stem->owidth = edge2->opos - edge1->opos;
+              stem->width  = edge2->pos  - edge1->pos;
+
+              /* compute min_coord and max_coord */
+              {
+                FT_Pos  min_coord = seg->min_coord;
+                FT_Pos  max_coord = seg->max_coord;
+
+                if (seg2->min_coord > min_coord)
+                  min_coord = seg2->min_coord;
+
+                if (seg2->max_coord < max_coord)
+                  max_coord = seg2->max_coord;
+
+                stem->min_coord = min_coord;
+                stem->max_coord = max_coord;
+              }
+
+              /* compute minimum and maximum positions for stem      */
+              /* note that the left-most/bottom-most stem has always */
+              /* a fixed position..                                  */
+              if (stem == stems || edge1->blue_edge || edge2->blue_edge)
+              {
+                /* this stem cannot move, it is snapped to a blue edge */
+                stem->min_pos = stem->pos;
+                stem->max_pos = stem->pos;
+              }
+              else
+              {
+                /* this edge can move, compute its min and max positions */
+                FT_Pos  pos1 = stem->opos;
+                FT_Pos  pos2 = pos1 + stem->owidth - stem->width;
+                FT_Pos  min1 = (pos1 & -64);
+                FT_Pos  min2 = (pos2 & -64);
+
+                stem->min_pos = min1;
+                stem->max_pos = min1+64;
+                if (min2 < min1)
+                  stem->min_pos = min2;
+                else
+                  stem->max_pos = min2+64;
+
+                /* XXX : just to see what it does */
+                stem->max_pos += 64;
+
+                /* just for the case where direct hinting did some incredible */
+                /* things (e.g. blue edge shifts..)                           */
+                if (stem->min_pos > stem->pos)
+                  stem->min_pos = stem->pos;
+
+                if (stem->max_pos < stem->pos)
+                  stem->max_pos = stem->pos;
+              }
+
+              stem->velocity = 0;
+              stem->force    = 0;
+
+              stem++;
+            }
+            seg = seg->edge_next;
+          }
+          while (seg != edge->first);
+        }
+      }
+
+      *p_stems     = stems;
+      *p_num_stems = num_stems;
+
+      edges      = outline->vert_edges;
+      edge_limit = edges + outline->num_vedges;
+      scale      = outline->x_scale;
+
+      p_stems     = &optimizer->vert_stems;
+      p_num_stems = &optimizer->num_vstems;
+    }
+  Exit:
+    #ifdef DEBUG_OPTIM
+    AH_Dump_Stems(optimizer);
+    #endif
+    return error;
+  }
+
+
+  /* returns the spring area between two stems, 0 if none */
+  static
+  FT_Pos  stem_spring_area( AH_Stem*  stem1, AH_Stem*  stem2 )
+  {
+    FT_Pos  area1 = stem1->max_coord - stem1->min_coord;
+    FT_Pos  area2 = stem2->max_coord - stem2->min_coord;
+    FT_Pos  min   = stem1->min_coord;
+    FT_Pos  max   = stem1->max_coord;
+    FT_Pos  area;
+
+    /* order stems */
+    if (stem2->opos <= stem1->opos + stem1->owidth)
+      return 0;
+
+    if (min < stem2->min_coord)
+      min = stem2->min_coord;
+
+    if (max < stem2->max_coord)
+      max = stem2->max_coord;
+
+    area = (max-min);
+    if ( 2*area < area1 && 2*area < area2 )
+      area = 0;
+
+    return area;
+  }
+
+
+  /* compute all springs in an outline */
+  static
+  int  optim_compute_springs( AH_Optimizer*  optimizer )
+  {
+    /* basically, a spring exists between two stems if most of their */
+    /* surface is aligned..                                          */
+    FT_Memory    memory  = optimizer->memory;
+
+    AH_Stem*     stems;
+    AH_Stem*     stem_limit;
+    AH_Stem*     stem;
+    int          dimension;
+    int          error = 0;
+
+    FT_Int*      p_num_springs;
+    AH_Spring**  p_springs;
+
+    stems      = optimizer->horz_stems;
+    stem_limit = stems + optimizer->num_hstems;
+
+    p_springs     = &optimizer->horz_springs;
+    p_num_springs = &optimizer->num_hsprings;
+
+    for ( dimension = 1; dimension >= 0; dimension-- )
+    {
+      FT_Int      num_springs = 0;
+      AH_Spring*  springs     = 0;
+
+      /* first of all, count stem springs */
+      for ( stem = stems; stem+1 < stem_limit; stem++ )
+      {
+        AH_Stem*  stem2;
+        for ( stem2 = stem+1; stem2 < stem_limit; stem2++ )
+          if (stem_spring_area(stem,stem2))
+            num_springs++;
+      }
+
+      /* then allocate and build the springs table */
+      if (num_springs > 0)
+      {
+        AH_Spring*  spring;
+
+        /* allocate table of springs */
+        if ( ALLOC_ARRAY( springs, num_springs, AH_Spring ) )
+          goto Exit;
+
+        /* fill the springs table */
+        spring = springs;
+        for ( stem = stems; stem+1 < stem_limit; stem++ )
+        {
+          AH_Stem*  stem2;
+          FT_Pos    area;
+
+          for ( stem2 = stem+1; stem2 < stem_limit; stem2++ )
+          {
+            area = stem_spring_area(stem,stem2);
+            if (area)
+            {
+              /* add a new spring here */
+              spring->stem1   = stem;
+              spring->stem2   = stem2;
+              spring->owidth  = stem2->opos - (stem->opos + stem->owidth);
+              spring->tension = 0;
+
+              spring++;
+            }
+          }
+        }
+      }
+      *p_num_springs = num_springs;
+      *p_springs     = springs;
+
+      stems      = optimizer->vert_stems;
+      stem_limit = stems + optimizer->num_vstems;
+
+      p_springs     = &optimizer->vert_springs;
+      p_num_springs = &optimizer->num_vsprings;
+    }
+
+  Exit:
+    #ifdef DEBUG_OPTIM
+    AH_Dump_Springs(optimizer);
+    #endif
+    return error;
+  }
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****   OPTIMISE THROUGH MY STRANGE SIMULATED ANNEALING ALGO ;-)      ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+#ifndef BRUTE_FORCE
+  /* compute all spring tensions */
+  static
+  void  optim_compute_tensions( AH_Optimizer*  optimizer )
+  {
+    AH_Spring*  spring = optimizer->springs;
+    AH_Spring*  limit  = spring + optimizer->num_springs;
+    for ( ; spring < limit; spring++ )
+    {
+      AH_Stem*  stem1 = spring->stem1;
+      AH_Stem*  stem2 = spring->stem2;
+      FT_Int    status;
+
+      FT_Pos  width;
+      FT_Pos  tension;
+      FT_Pos  sign;
+
+      /* compute the tension, it simply is -K*(new_width-old_width) */
+      width   =  stem2->pos - (stem1->pos + stem1->width);
+      tension =  width - spring->owidth;
+
+      sign = 1;
+      if (tension < 0)
+      {
+        sign    = -1;
+        tension = -tension;
+      }
+
+      if (width <= 0)
+        tension = 32000;
+      else
+        tension = (tension << 10)/width;
+
+      tension = -sign*FT_MulFix( tension, optimizer->tension_scale );
+      spring->tension = tension;
+
+      /* now, distribute tension among the englobing stems, if they */
+      /* are able to move..                                         */
+      status = 0;
+      if (stem1->pos <= stem1->min_pos)
+        status |= 1;
+      if (stem2->pos >= stem2->max_pos)
+        status |= 2;
+
+      if (!status)
+        tension /= 2;
+
+      if ((status & 1)== 0)
+        stem1->force -= tension;
+
+      if ((status & 2)== 0)
+        stem2->force += tension;
+    }
+  }
+
+
+
+  /* compute all stem movements - returns 0 if nothing moved */
+  static
+  int   optim_compute_stem_movements( AH_Optimizer*  optimizer )
+  {
+    AH_Stem*  stems = optimizer->stems;
+    AH_Stem*  limit = stems + optimizer->num_stems;
+    AH_Stem*  stem  = stems;
+    int       moved = 0;
+
+    /* set initial forces to velocity */
+    for ( stem = stems; stem < limit; stem++ )
+    {
+      stem->force     = stem->velocity;
+      stem->velocity /= 2;  /* XXXX: Heuristics */
+    }
+
+    /* compute the sum of forces applied on each stem */
+    optim_compute_tensions( optimizer );
+    #ifdef DEBUG_OPTIM
+    AH_Dump_Springs( optimizer );
+    AH_Dump_Stems2( optimizer );
+    #endif
+
+    /* now, see if something can move ? */
+    for ( stem = stems; stem < limit; stem++ )
+    {
+      if (stem->force > optimizer->tension_threshold)
+      {
+        /* there is enough tension to move the stem to the right */
+        if (stem->pos < stem->max_pos)
+        {
+          stem->pos += 64;
+          stem->velocity = stem->force/2;
+          moved = 1;
+        }
+        else
+          stem->velocity = 0;
+      }
+      else if (stem->force < optimizer->tension_threshold)
+      {
+        /* there is enough tension to move the stem to the left */
+        if (stem->pos > stem->min_pos)
+        {
+          stem->pos -= 64;
+          stem->velocity = stem->force/2;
+          moved = 1;
+        }
+        else
+          stem->velocity = 0;
+      }
+    }
+    /* return 0 if nothing moved */
+    return moved;
+  }
+
+#endif /* BRUTE_FORCE */
+
+
+  /* compute current global distortion from springs */
+  static
+  FT_Pos  optim_compute_distorsion( AH_Optimizer*  optimizer )
+  {
+    AH_Spring*  spring = optimizer->springs;
+    AH_Spring*  limit  = spring + optimizer->num_springs;
+    FT_Pos      distorsion = 0;
+
+    for ( ; spring < limit; spring++ )
+    {
+      AH_Stem*  stem1 = spring->stem1;
+      AH_Stem*  stem2 = spring->stem2;
+      FT_Pos  width;
+
+      width   =  stem2->pos - (stem1->pos + stem1->width);
+      width  -= spring->owidth;
+      if (width < 0)
+        width = -width;
+
+      distorsion += width;
+    }
+    return distorsion;
+  }
+
+
+  /* record stems configuration in "best of" history */
+  static
+  void  optim_record_configuration( AH_Optimizer*  optimizer )
+  {
+    FT_Pos             distorsion;
+    AH_Configuration*  configs = optimizer->configs;
+    AH_Configuration*  limit   = configs + optimizer->num_configs;
+    AH_Configuration*  config;
+
+    distorsion = optim_compute_distorsion( optimizer );
+    LOG(( "config distorsion = %.1f ", FLOAT(distorsion*64) ));
+
+    /* check that we really need to add this configuration to our */
+    /* sorted history..                                           */
+    if ( limit > configs && limit[-1].distorsion < distorsion )
+    {
+      LOG(( "ejected\n" ));
+      return;
+    }
+
+    /* add new configuration at the end of the table */
+    {
+      int  n;
+
+      config = limit;
+      if (optimizer->num_configs < AH_MAX_CONFIGS)
+        optimizer->num_configs++;
+      else
+        config--;
+
+      config->distorsion = distorsion;
+
+      for ( n = 0; n < optimizer->num_stems; n++ )
+        config->positions[n] = optimizer->stems[n].pos;
+    }
+
+    /* move the current configuration towards the front of the list */
+    /* when necessary, yes this is slow bubble sort ;-)             */
+    while ( config > configs && config[0].distorsion < config[-1].distorsion )
+    {
+      AH_Configuration  temp;
+      config--;
+      temp      = config[0];
+      config[0] = config[1];
+      config[1] = temp;
+    }
+    LOG(( "recorded !!\n" ));
+  }
+
+
+#ifdef BRUTE_FORCE
+  /* optimize outline in a single direction */
+  static
+  void  optim_compute( AH_Optimizer*  optimizer )
+  {
+    int      n;
+    FT_Bool  moved;
+
+
+    AH_Stem*  stem  = optimizer->stems;
+    AH_Stem*  limit = stem + optimizer->num_stems;
+
+    /* empty, exit */
+    if (stem >= limit)
+      return;
+
+    optimizer->num_configs = 0;
+
+    stem = optimizer->stems;
+    for ( ; stem < limit; stem++ )
+      stem->pos = stem->min_pos;
+
+    do
+    {
+      /* record current configuration */
+      optim_record_configuration(optimizer);
+
+      /* now change configuration */
+      moved = 0;
+      for ( stem = optimizer->stems; stem < limit; stem++ )
+      {
+        if (stem->pos < stem->max_pos)
+        {
+          stem->pos += 64;
+          moved      = 1;
+          break;
+        }
+
+        stem->pos = stem->min_pos;
+      }
+    }
+    while (moved);
+
+    /* now, set the best stem positions */
+    for ( n = 0; n < optimizer->num_stems; n++ )
+    {
+      AH_Stem*  stem = optimizer->stems + n;
+      FT_Pos    pos  = optimizer->configs[0].positions[n];
+
+      stem->edge1->pos = pos;
+      stem->edge2->pos = pos + stem->width;
+
+      stem->edge1->flags |= ah_edge_done;
+      stem->edge2->flags |= ah_edge_done;
+    }
+  }
+#else
+  /* optimize outline in a single direction */
+  static
+  void  optim_compute( AH_Optimizer*  optimizer )
+  {
+    int  n, counter, counter2;
+
+    optimizer->num_configs       = 0;
+    optimizer->tension_scale     = 0x80000L;
+    optimizer->tension_threshold = 64;
+
+    /* record initial configuration threshold */
+    optim_record_configuration(optimizer);
+    counter = 0;
+    for ( counter2 = optimizer->num_stems*8; counter2 >= 0; counter2-- )
+    {
+      if (counter == 0)
+        counter = 2*optimizer->num_stems;
+
+      if (!optim_compute_stem_movements( optimizer ))
+        break;
+
+      optim_record_configuration(optimizer);
+      counter--;
+      if (counter == 0)
+        optimizer->tension_scale /= 2;
+    }
+
+    /* now, set the best stem positions */
+    for ( n = 0; n < optimizer->num_stems; n++ )
+    {
+      AH_Stem*  stem = optimizer->stems + n;
+      FT_Pos    pos  = optimizer->configs[0].positions[n];
+
+      stem->edge1->pos = pos;
+      stem->edge2->pos = pos + stem->width;
+
+      stem->edge1->flags |= ah_edge_done;
+      stem->edge2->flags |= ah_edge_done;
+    }
+  }
+#endif
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****   HIGH-LEVEL OPTIMIZER API                                      ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+
+  /* releases the optimisation data */
+  void AH_Optimizer_Done( AH_Optimizer*  optimizer )
+  {
+    if (optimizer)
+    {
+      FT_Memory  memory = optimizer->memory;
+      FREE( optimizer->horz_stems );
+      FREE( optimizer->vert_stems );
+      FREE( optimizer->horz_springs );
+      FREE( optimizer->vert_springs );
+      FREE( optimizer->positions );
+    }
+  }
+
+  /* loads the outline into the optimizer */
+  int  AH_Optimizer_Init( AH_Optimizer*  optimizer,
+                          AH_Outline*    outline,
+                          FT_Memory      memory )
+  {
+    FT_Error  error;
+
+    MEM_Set( optimizer, 0, sizeof(*optimizer));
+    optimizer->outline = outline;
+    optimizer->memory  = memory;
+
+    LOG(( "initializing new optimizer\n" ));
+    /* compute stems and springs */
+    error = optim_compute_stems  ( optimizer ) ||
+            optim_compute_springs( optimizer );
+    if (error) goto Fail;
+
+    /* allocate stem positions history and configurations */
+    {
+      int  n, max_stems;
+
+      max_stems = optimizer->num_hstems;
+      if (max_stems < optimizer->num_vstems)
+        max_stems = optimizer->num_vstems;
+
+      if ( ALLOC_ARRAY( optimizer->positions, max_stems*AH_MAX_CONFIGS, FT_Pos ) )
+        goto Fail;
+
+      optimizer->num_configs = 0;
+      for ( n = 0; n < AH_MAX_CONFIGS; n++ )
+        optimizer->configs[n].positions = optimizer->positions + n*max_stems;
+    }
+
+    return error;
+
+  Fail:
+    AH_Optimizer_Done( optimizer );
+    return error;
+  }
+
+
+  /* compute optimal outline */
+  void  AH_Optimizer_Compute( AH_Optimizer*  optimizer )
+  {
+    optimizer->num_stems   = optimizer->num_hstems;
+    optimizer->stems       = optimizer->horz_stems;
+    optimizer->num_springs = optimizer->num_hsprings;
+    optimizer->springs     = optimizer->horz_springs;
+
+    if (optimizer->num_springs > 0)
+    {
+      LOG(( "horizontal optimisation ------------------------\n" ));
+      optim_compute( optimizer );
+    }
+
+    optimizer->num_stems   = optimizer->num_vstems;
+    optimizer->stems       = optimizer->vert_stems;
+    optimizer->num_springs = optimizer->num_vsprings;
+    optimizer->springs     = optimizer->vert_springs;
+
+    if (optimizer->num_springs)
+    {
+      LOG(( "vertical optimisation --------------------------\n" ));
+      optim_compute( optimizer );
+    }
+  }
+
+
+
+
+
--- /dev/null
+++ b/src/autohint/ahoptim.h
@@ -1,0 +1,137 @@
+/***************************************************************************/
+/*                                                                         */
+/*  FreeType Auto-Gridder Outline Optimisation                             */
+/*                                                                         */
+/*  This module is in charge of optimising the outlines produced by the    */
+/*  auto-hinter in direct mode. This is required at small pixel sizes in   */
+/*  order to ensure coherent spacing, among other things..                 */
+/*                                                                         */
+/*  The technique used in this module is a simplified simulated annealing. */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+
+#ifndef AGOPTIM_H
+#define AGOPTIM_H
+
+#ifdef FT_FLAT_COMPILE
+#include "ahtypes.h"
+#else
+#include <autohint/ahtypes.h>
+#endif
+
+/* the maximal number of stem configurations to record during optimisation */
+#define AH_MAX_CONFIGS  8
+
+
+  typedef struct AH_Stem_
+  {
+    FT_Pos       pos;       /* current position        */
+    FT_Pos       velocity;  /* current velocity        */
+    FT_Pos       force;     /* sum of current forces   */
+    FT_Pos       width;     /* normalized width        */
+
+    FT_Pos       min_pos;   /* minimum grid position */
+    FT_Pos       max_pos;   /* maximum grid position */
+
+    AH_Edge*     edge1;     /* left/bottom edge */
+    AH_Edge*     edge2;     /* right/top edge   */
+
+    FT_Pos       opos;      /* original position */
+    FT_Pos       owidth;    /* original width    */
+
+    FT_Pos       min_coord; /* minimum coordinate */
+    FT_Pos       max_coord; /* maximum coordinate */
+
+  } AH_Stem;
+
+
+  /* A spring between two stems */
+  typedef struct AH_Spring_
+  {
+    AH_Stem*  stem1;
+    AH_Stem*  stem2;
+    FT_Pos    owidth;   /* original width  */
+    FT_Pos    tension;  /* current tension */
+
+  } AH_Spring;
+
+
+  /* A configuration records the position of each stem at a given time  */
+  /* as well as the associated distortion..                             */
+  typedef struct AH_Configuration_
+  {
+    FT_Pos*  positions;
+    FT_Long  distorsion;
+
+  } AH_Configuration;
+
+
+
+
+  typedef struct AH_Optimizer_
+  {
+    FT_Memory    memory;
+    AH_Outline*  outline;
+
+    FT_Int       num_hstems;
+    AH_Stem*     horz_stems;
+
+    FT_Int       num_vstems;
+    AH_Stem*     vert_stems;
+
+    FT_Int       num_hsprings;
+    FT_Int       num_vsprings;
+    AH_Spring*   horz_springs;
+    AH_Spring*   vert_springs;
+
+    FT_Int            num_configs;
+    AH_Configuration  configs[ AH_MAX_CONFIGS ];
+    FT_Pos*           positions;
+
+    /* during each pass, use these instead */
+    FT_Int       num_stems;
+    AH_Stem*     stems;
+
+    FT_Int       num_springs;
+    AH_Spring*   springs;
+    FT_Bool      vertical;
+
+    FT_Fixed     tension_scale;
+    FT_Pos       tension_threshold;
+
+  } AH_Optimizer;
+
+
+  /* loads the outline into the optimizer */
+  extern
+  int  AH_Optimizer_Init( AH_Optimizer*  optimizer,
+                          AH_Outline*    outline,
+                          FT_Memory      memory );
+
+
+
+  /* compute optimal outline */
+  extern
+  void  AH_Optimizer_Compute( AH_Optimizer*  optimizer );
+
+
+
+
+  /* releases the optimisation data */
+  extern
+  void AH_Optimizer_Done( AH_Optimizer*  optimizer );
+
+
+#endif /* AGOPTIM_H */
--- /dev/null
+++ b/src/autohint/ahtypes.h
@@ -1,0 +1,440 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ahtypes.h                                                              */
+/*                                                                         */
+/*  General types and definitions for the auto-hint module                 */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+
+#ifndef AGTYPES_H
+#define AGTYPES_H
+
+#include <freetype/internal/ftobjs.h>  /* for freetype.h + LOCAL_DEF etc.. */
+
+#ifdef FT_FLAT_COMPILE
+#include "ahloader.h"  /* glyph loader types & declarations */
+#else
+#include <autohint/ahloader.h>  /* glyph loader types & declarations */
+#endif
+
+#define xxDEBUG_AG
+
+#ifdef DEBUG_AG
+#include <stdio.h>
+#define AH_LOG(x)  printf##x
+#else
+#define AH_LOG(x)  /* nothing */
+#endif
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/****                                                                   ****/
+/****   COMPILE-TIME BUILD OPTIONS                                      ****/
+/****                                                                   ****/
+/****   Toggle these configuration macros to experiment with            ****/
+/****   "features" of the auto-hinter..                                 ****/
+/****                                                                   ****/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/* if this option is defined, only strong interpolation will be used to    */
+/* place the points between edges. Otherwise, "smooth" points are detected */
+/* and later hinted through weak interpolation to correct some unpleasant  */
+/* artefacts..                                                             */
+/*                                                                         */
+#undef  AH_OPTION_NO_WEAK_INTERPOLATION
+#undef  AH_OPTION_NO_STRONG_INTERPOLATION
+
+/* undefine this macro if you don't want to hint the metrics */
+/* there is no reason to do this, except for experimentation */
+#define AH_HINT_METRICS
+
+/* define this macro if you do not want to insert extra edges at a glyph's */
+/* x and y extrema (when there isn't one already available). This help     */
+/* reduce a number of artefacts and allow hinting of metrics..             */
+/*                                                                         */
+#undef  AH_OPTION_NO_EXTREMUM_EDGES
+
+/* don't touch for now.. */
+#define AH_MAX_WIDTHS   12
+#define AH_MAX_HEIGHTS  12
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/****                                                                   ****/
+/****   TYPES DEFINITIONS                                               ****/
+/****                                                                   ****/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+ /* see agangles.h */
+  typedef FT_Int   AH_Angle;
+
+
+ /* hint flags */
+  typedef enum AH_Flags_
+  {
+    ah_flah_none     = 0,
+
+    /* bezier control points flags */
+    ah_flah_conic    = 1,
+    ah_flah_cubic    = 2,
+    ah_flah_control  = ah_flah_conic | ah_flah_cubic,
+
+    /* extrema flags */
+    ah_flah_extrema_x = 4,
+    ah_flah_extrema_y = 8,
+
+    /* roundness */
+    ah_flah_round_x   = 16,
+    ah_flah_round_y   = 32,
+
+    /* touched */
+    ah_flah_touch_x   = 64,
+    ah_flah_touch_y   = 128,
+
+    /* weak interpolation */
+    ah_flah_weak_interpolation = 256,
+
+    /* never remove this one !! */
+    ah_flah_max
+
+  } AH_Flags;
+
+
+ /* edge hint flags */
+  typedef enum AH_Edge_Flags_
+  {
+    ah_edge_normal = 0,
+    ah_edge_round  = 1,
+    ah_edge_serif  = 2,
+    ah_edge_done   = 4
+
+  } AH_Edge_Flags;
+
+
+ /* hint directions - the values are computed so that two vectors are */
+ /* in opposite directions iff "dir1+dir2 == 0"                       */
+  typedef enum AH_Direction_
+  {
+    ah_dir_none  =  4,
+    ah_dir_right =  1,
+    ah_dir_left  = -1,
+    ah_dir_up    =  2,
+    ah_dir_down  = -2
+
+  } AH_Direction;
+
+
+  typedef struct AH_Point    AH_Point;
+  typedef struct AH_Segment  AH_Segment;
+  typedef struct AH_Edge     AH_Edge;
+
+ /***************************************************************************
+  *
+  * <Struct>
+  *    AH_Point
+  *
+  * <Description>
+  *    A structure used to model an outline point to the AH_Outline type
+  *
+  * <Fields>
+  *    flags   :: current point hint flags
+  *    ox, oy  :: current original scaled coordinates
+  *    fx, fy  :: current coordinates in font units
+  *    x,  y   :: current hinter coordinates
+  *    u, v    :: point coordinates - meaning varies with context
+  *
+  *    in_dir  :: direction of inwards  vector (prev->point)
+  *    out_dir :: direction of outwards vector (point->next)
+  *
+  *    in_angle  :: angle of inwards vector
+  *    out_angle :: angle of outwards vector
+  *
+  *    next    :: next point in same contour
+  *    prev    :: previous point in same contour
+  *
+  */
+  struct AH_Point
+  {
+    AH_Flags      flags;   /* point flags used by hinter         */
+    FT_Pos        ox, oy;
+    FT_Pos        fx, fy;
+    FT_Pos        x,  y;
+    FT_Pos        u,  v;
+
+    AH_Direction  in_dir;   /* direction of inwards vector  */
+    AH_Direction  out_dir;  /* direction of outwards vector */
+
+    AH_Angle      in_angle;
+    AH_Angle      out_angle;
+
+    AH_Point*     next;    /* next point in contour      */
+    AH_Point*     prev;    /* previous point in contour  */
+  };
+
+
+ /***************************************************************************
+  *
+  * <Struct>
+  *    AH_Segment
+  *
+  * <Description>
+  *    a structure used to describe an edge segment to the auto-hinter. A
+  *    segment is simply a sequence of successive points located on the same
+  *    horizontal or vertical "position", in a given direction.
+  *
+  * <Fields>
+  *    flags      :: segment edge flags ( straight, rounded.. )
+  *    dir        :: segment direction
+  *
+  *    first      :: first point in segment
+  *    last       :: last point in segment
+  *    contour    :: ptr to first point of segment's contour
+  *
+  *    pos        :: segment position in font units
+  *    size       :: segment size
+  *
+  *    edge       :: edge of current segment
+  *    edge_next  :: next segment on same edge
+  *
+  *    link       :: the pairing segment for this edge
+  *    serif      :: the primary segment for serifs
+  *    num_linked :: the number of other segments that link to this one
+  *
+  *    score      :: used to score the segment when selecting them..
+  *
+  */
+  struct AH_Segment
+  {
+    AH_Edge_Flags  flags;
+    AH_Direction   dir;
+
+    AH_Point*      first;     /* first point in edge segment      */
+    AH_Point*      last;      /* last point in edge segment       */
+    AH_Point**     contour;   /* ptr to first point of segment's contour */
+
+    FT_Pos         pos;       /* position of segment */
+    FT_Pos         min_coord; /* minimum coordinate of segment */
+    FT_Pos         max_coord; /* maximum coordinate of segment */
+
+    AH_Edge*       edge;
+    AH_Segment*    edge_next;
+
+    AH_Segment*    link;         /* link segment               */
+    AH_Segment*    serif;        /* primary segment for serifs */
+    FT_Pos         num_linked;   /* number of linked segments  */
+    FT_Int         score;
+  };
+
+
+ /***************************************************************************
+  *
+  * <Struct>
+  *    AH_Edge
+  *
+  * <Description>
+  *    a structure used to describe an edge, which really is a horizontal
+  *    or vertical coordinate which will be hinted depending on the segments
+  *    located on it..
+  *
+  * <Fields>
+  *    flags      :: segment edge flags ( straight, rounded.. )
+  *    dir        :: main segment direction on this edge
+  *
+  *    first      :: first edge segment
+  *    last       :: last edge segment
+  *
+  *    fpos       :: original edge position in font units
+  *    opos       :: original scaled edge position
+  *    pos        :: hinted edge position
+  *
+  *    link       :: the linked edge
+  *    serif      :: the serif edge
+  *    num_paired :: the number of other edges that pair to this one
+  *
+  *    score      :: used to score the edge when selecting them..
+  *
+  *    blue_edge  :: indicate the blue zone edge this edge is related to
+  *                  only set for some of the horizontal edges in a Latin
+  *                  font..
+  *
+  ***************************************************************************/
+  struct AH_Edge
+  {
+    AH_Edge_Flags   flags;
+    AH_Direction    dir;
+
+    AH_Segment*     first;
+    AH_Segment*     last;
+
+    FT_Pos          fpos;
+    FT_Pos          opos;
+    FT_Pos          pos;
+
+    AH_Edge*        link;
+    AH_Edge*        serif;
+    FT_Int          num_linked;
+
+    FT_Int          score;
+    FT_Pos*         blue_edge;
+  };
+
+
+ /* an outline as seen by the hinter */
+  typedef struct AH_Outline_
+  {
+    FT_Memory    memory;
+
+    AH_Direction vert_major_dir;   /* vertical major direction   */
+    AH_Direction horz_major_dir;   /* horizontal major direction */
+
+    FT_Fixed     x_scale;
+    FT_Fixed     y_scale;
+    FT_Pos       edge_distance_threshold;
+
+    FT_Int       max_points;
+    FT_Int       num_points;
+    AH_Point*    points;
+
+    FT_Int       max_contours;
+    FT_Int       num_contours;
+    AH_Point**   contours;
+
+    FT_Int       num_hedges;
+    AH_Edge*     horz_edges;
+
+    FT_Int       num_vedges;
+    AH_Edge*     vert_edges;
+
+    FT_Int       num_hsegments;
+    AH_Segment*  horz_segments;
+
+    FT_Int       num_vsegments;
+    AH_Segment*  vert_segments;
+
+  } AH_Outline;
+
+
+
+  typedef enum AH_Blue_
+  {
+    ah_blue_capital_top,     /* THEZOCQS */
+    ah_blue_capital_bottom,  /* HEZLOCUS */
+    ah_blue_small_top,       /* xzroesc  */
+    ah_blue_small_bottom,    /* xzroesc  */
+    ah_blue_small_minor,     /* pqgjy    */
+
+    ah_blue_max
+
+  } AH_Blue;
+
+  typedef enum
+  {
+    ah_hinter_monochrome = 1,
+    ah_hinter_optimize   = 2
+
+  } AH_Hinter_Flags;
+
+
+ /************************************************************************
+  *
+  *  <Struct>
+  *     AH_Globals
+  *
+  *  <Description>
+  *     Holds the global metrics for a given font face (be it in design
+  *     units, or scaled pixel values)..
+  *
+  *  <Fields>
+  *     num_widths  :: number of widths
+  *     num_heights :: number of heights
+  *     widths      :: snap widths, including standard one
+  *     heights     :: snap height, including standard one
+  *     blue_refs   :: reference position of blue zones
+  *     blue_shoots :: overshoot position of blue zones
+  *
+  ************************************************************************/
+
+  typedef struct AH_Globals_
+  {
+    FT_Int    num_widths;
+    FT_Int    num_heights;
+
+    FT_Pos    widths [ AH_MAX_WIDTHS ];
+    FT_Pos    heights[ AH_MAX_HEIGHTS ];
+
+    FT_Pos    blue_refs  [ ah_blue_max ];
+    FT_Pos    blue_shoots[ ah_blue_max ];
+
+  } AH_Globals;
+
+
+ /************************************************************************
+  *
+  *  <Struct>
+  *     AH_Face_Globals
+  *
+  *  <Description>
+  *     Holds the complete global metrics for a given font face (i.e. the
+  *     design units version + a scaled version + the current scales used)
+  *
+  *  <Fields>
+  *     face     :: handle to source face object
+  *     design   :: globals in font design units
+  *     scaled   :: scaled globals in sub-pixel values
+  *     x_scale  :: current horizontal scale
+  *     y_scale  :: current vertical scale
+  *
+  ************************************************************************/
+
+  typedef struct AH_Face_Globals_
+  {
+    FT_Face     face;
+    AH_Globals  design;
+    AH_Globals  scaled;
+    FT_Fixed    x_scale;
+    FT_Fixed    y_scale;
+    FT_Bool     control_overshoot;
+
+  } AH_Face_Globals;
+
+
+
+
+  typedef struct AH_Hinter
+  {
+    FT_Memory         memory;
+    FT_Long           flags;
+
+    FT_Int            algorithm;
+    FT_Face           face;
+
+    AH_Face_Globals*  globals;
+
+    AH_Outline*       glyph;
+
+    AH_Loader*        loader;
+    FT_Vector         pp1;
+    FT_Vector         pp2;
+
+  } AH_Hinter;
+
+#endif /* AGTYPES_H */
--- /dev/null
+++ b/src/autohint/autohint.c
@@ -1,0 +1,40 @@
+/***************************************************************************/
+/*                                                                         */
+/*  autohint.c                                                             */
+/*                                                                         */
+/*    Automatic Hinting wrapper.                                           */
+/*                                                                         */
+/*  Copyright 2000: Catharon Productions Inc.                              */
+/*  Author: David Turner                                                   */
+/*                                                                         */
+/*  This file is part of the Catharon Typography Project and shall only    */
+/*  be used, modified, and distributed under the terms of the Catharon     */
+/*  Open Source License that should come with this file under the name     */
+/*  "CatharonLicense.txt". By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/*  Note that this license is compatible with the FreeType license         */
+/*                                                                         */
+/***************************************************************************/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#ifdef FT_FLAT_COMPILE
+
+#include "ahangles.c"
+#include "ahglyph.c"
+#include "ahglobal.c"
+#include "ahhint.c"
+#include "ahmodule.c"
+
+#else
+
+#include <autohint/ahangles.c>
+#include <autohint/ahglyph.c>
+#include <autohint/ahglobal.c>
+#include <autohint/ahhint.c>
+#include <autohint/ahmodule.c>
+
+#endif
+
--- /dev/null
+++ b/src/autohint/mather.py
@@ -1,0 +1,54 @@
+import math
+
+ag_pi = 256
+
+def print_arctan( atan_bits ):
+    atan_base = 1 << atan_bits
+    
+    print "static AH_Angle  ag_arctan[ 1L << AG_ATAN_BITS ] ="
+    print "{"
+    
+    count = 0
+    line  = ""
+    
+    for n in range(atan_base):
+        comma = ","
+        if (n == atan_base-1):
+            comma = ""
+            
+        angle = math.atan(n*1.0/atan_base)/math.pi*ag_pi
+        line  = line + " " + repr(int(angle+0.5)) + comma
+        count = count+1;
+        if (count == 8):
+            count = 0
+            print line
+            line = ""
+            
+    if (count >0):
+        print line
+    print "};"
+
+
+def print_sines():
+    print "static FT_Fixed  ah_sines[ AG_HALF_PI+1 ] ="
+    print "{"
+    count = 0
+    line  = ""
+    
+    for n in range(ag_pi/2):
+        sinus = math.sin(n*math.pi/ag_pi)
+        line  = line + " " + repr(int(65536.0*sinus)) + ","
+        count = count+1
+        if (count == 8):
+            count = 0
+            print line
+            line = ""
+        
+    if (count > 0):
+        print line
+    print " 65536"
+    print "};"
+
+print_arctan(8)
+print
+
--- /dev/null
+++ b/src/autohint/module.mk
@@ -1,0 +1,7 @@
+make_module_list: add_autohint_module
+
+add_autohint_module:
+	$(OPEN_DRIVER)autohint_module_class$(CLOSE_DRIVER)
+	$(ECHO_DRIVER)autohint  $(ECHO_DRIVER_DESC)automatic hinting module$(ECHO_DRIVER_DONE)
+
+# EOF
--- /dev/null
+++ b/src/autohint/rules.mk
@@ -1,0 +1,86 @@
+#
+# FreeType 2 auto-hinter module configuration rules
+#
+
+
+#
+#  Copyright 2000: Catharon Productions Inc.
+#  Author: David Turner
+#
+#  This file is part of the Catharon Typography Project and shall only
+#  be used, modified, and distributed under the terms of the Catharon
+#  Open Source License that should come with this file under the name
+#  "CatharonLicense.txt". By continuing to use, modify, or distribute
+#  this file you indicate that you have read the license and
+#  understand and accept it fully.
+#
+#  Note that this license is compatible with the FreeType license
+#
+#
+# Copyright 1996-2000 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# AUTO driver directory
+#
+AUTO_DIR  := $(SRC_)autohint
+AUTO_DIR_ := $(AUTO_DIR)$(SEP)
+
+
+# compilation flags for the driver
+#
+AUTO_COMPILE := $(FT_COMPILE)
+
+
+# AUTO driver sources (i.e., C files)
+#
+AUTO_DRV_SRC := $(AUTO_DIR_)ahangles.c  \
+                $(AUTO_DIR_)ahglobal.c  \
+                $(AUTO_DIR_)ahglyph.c   \
+                $(AUTO_DIR_)ahhint.c    \
+                $(AUTO_DIR_)ahmodule.c
+
+# AUTO driver headers
+#
+AUTO_DRV_H := $(AUTO_DRV_SRC:%c=%h)
+
+
+# AUTO driver object(s)
+#
+#   AUTO_DRV_OBJ_M is used during `multi' builds.
+#   AUTO_DRV_OBJ_S is used during `single' builds.
+#
+AUTO_DRV_OBJ_M := $(AUTO_DRV_SRC:$(AUTO_DIR_)%.c=$(OBJ_)%.$O)
+AUTO_DRV_OBJ_S := $(OBJ_)autohint.$O
+
+# AUTO driver source file for single build
+#
+AUTO_DRV_SRC_S := $(AUTO_DIR_)autohint.c
+
+
+# AUTO driver - single object
+#
+$(AUTO_DRV_OBJ_S): $(AUTO_DRV_SRC_S) $(AUTO_DRV_SRC) \
+                   $(FREETYPE_H) $(AUTO_DRV_H)
+	$(AUTO_COMPILE) $T$@ $(AUTO_DRV_SRC_S)
+
+
+# AUTO driver - multiple objects
+#
+$(OBJ_)%.$O: $(AUTO_DIR_)%.c $(FREETYPE_H) $(AUTO_DRV_H)
+	$(AUTO_COMPILE) $T$@ $<
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(AUTO_DRV_OBJ_S)
+DRV_OBJS_M += $(AUTO_DRV_OBJ_M)
+
+
+# EOF