shithub: freetype+ttf2subf

Download patch

ref: 0d73b0c49abc97cb89d05bb5295769f289d271e2
parent: b7e18efcd2d6a71ec1a4bdf167f78d707ac91593
author: David Turner <[email protected]>
date: Wed Jun 26 19:45:21 EDT 2002

* include/freetype/internal/ftobject.h: updating the object sub-system
    definitions (still experimental)

    * src/base/fthash.c (ft_hash_remove): fixing a small reallocation bug

    * include/freetype/fttrigon.h, src/base/fttrigon.c: adding
    FT_Vector_From_Polar and FT_Angle_Diff to the trigonometric functions

    * include/freetype/ftstroker.h, src/base/ftstroker.c: adding path stroker
    component (work in progress)

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,20 @@
 2002-06-26  David Turner
 
-        * src/truetype/ttgload.c (TT_Load_Composite_Glyph),
-        src/base/ftoutln.c (FT_Vector_Transform): fixed Werner's latest
-        fix. FT_Vector_Transform wasn't buggy, the TrueType composite loader
-        was...
+    * include/freetype/internal/ftobject.h: updating the object sub-system
+    definitions (still experimental)
+
+    * src/base/fthash.c (ft_hash_remove): fixing a small reallocation bug
+
+    * include/freetype/fttrigon.h, src/base/fttrigon.c: adding
+    FT_Vector_From_Polar and FT_Angle_Diff to the trigonometric functions
+
+    * include/freetype/ftstroker.h, src/base/ftstroker.c: adding path stroker
+    component (work in progress)
+
+    * src/truetype/ttgload.c (TT_Load_Composite_Glyph),
+    src/base/ftoutln.c (FT_Vector_Transform): fixed Werner's latest
+    fix. FT_Vector_Transform wasn't buggy, the TrueType composite loader
+    was...
 
 2002-06-24  Werner Lemberg  <[email protected]>
 
--- a/include/freetype/ftstroker.h
+++ b/include/freetype/ftstroker.h
@@ -44,7 +44,7 @@
     FT_STROKER_LINEJOIN_ROUND = 0,
     FT_STROKER_LINEJOIN_BEVEL,
     FT_STROKER_LINEJOIN_MITER
-  
+
   } FT_Stroker_LineJoin;
 
 
@@ -74,10 +74,10 @@
     FT_STROKER_LINECAP_BUTT = 0,
     FT_STROKER_LINECAP_ROUND,
     FT_STROKER_LINECAP_SQUARE
-    
+
   } FT_Stroker_LineCap;
 
-  
+
   FT_EXPORT( FT_Error )
   FT_Stroker_New( FT_Memory    memory,
                   FT_Stroker  *astroker );
@@ -89,10 +89,15 @@
                   FT_Stroker_LineJoin  line_join,
                   FT_Fixed             miter_limit );
 
+
   FT_EXPORT( FT_Error )
+  FT_Stroker_ParseOutline( FT_Stroker   stroker,
+                           FT_Outline*  outline,
+                           FT_Bool      opened );
+
+  FT_EXPORT( FT_Error )
   FT_Stroker_BeginSubPath( FT_Stroker  stroker,
-                           FT_Pos      x,
-                           FT_Pos      y,
+                           FT_Vector*  to,
                            FT_Bool     open );
 
   FT_EXPORT( FT_Error )
@@ -101,24 +106,18 @@
 
   FT_EXPORT( FT_Error )
   FT_Stroker_LineTo( FT_Stroker  stroker,
-                     FT_Pos      to_x,
-                     FT_Pos      to_y );
+                     FT_Vector*  to );
 
   FT_EXPORT( FT_Error )
   FT_Stroker_ConicTo( FT_Stroker  stroker,
-                      FT_Pos      control_x,
-                      FT_Pos      control_y,
-                      FT_Pos      to_x,
-                      FT_Pos      to_y );
+                      FT_Vector*  control,
+                      FT_Vector*  to );
 
   FT_EXPORT( FT_Error )
   FT_Stroker_CubicTo( FT_Stroker  stroker,
-                      FT_Pos      control1_x,
-                      FT_Pos      control1_y,
-                      FT_Pos      control2_x,
-                      FT_Pos      control2_y,
-                      FT_Pos      to_x,
-                      FT_Pos      to_y );
+                      FT_Vector*  control1,
+                      FT_Vector*  control2,
+                      FT_Vector*  to );
 
 
   FT_EXPORT( FT_Error )
@@ -133,11 +132,6 @@
   FT_EXPORT( void )
   FT_Stroker_Done( FT_Stroker  stroker );
 
-
-  FT_EXPORT( FT_Error )
-  FT_Stroker_ParseOutline( FT_Stroker   stroker,
-                           FT_Outline*  outline,
-                           FT_Bool      opened );
 
 FT_END_HEADER
 
--- a/include/freetype/fttrigon.h
+++ b/include/freetype/fttrigon.h
@@ -291,6 +291,28 @@
   FT_Vector_Polarize( FT_Vector*  vec,
                       FT_Fixed   *length,
                       FT_Angle   *angle );
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* @function:                                                            */
+  /*    FT_Vector_From_Polar                                               */
+  /*                                                                       */
+  /* @description:                                                         */
+  /*    Compute vector coordinates from a length and angle.                */
+  /*                                                                       */
+  /* @output:                                                              */
+  /*    vec    :: The address of source vector.                            */
+  /*                                                                       */
+  /* @input:                                                               */
+  /*    length :: The vector length.                                       */
+  /*    angle  :: The vector angle.                                        */
+  /*                                                                       */
+  FT_EXPORT( void )
+  FT_Vector_From_Polar( FT_Vector*  vec,
+                        FT_Fixed    length,
+                        FT_Angle    angle );
+
   /* */
 
 
--- a/include/freetype/internal/fthash.h
+++ b/include/freetype/internal/fthash.h
@@ -59,9 +59,21 @@
 
  /***********************************************************
   *
-  * @type: FT_Hash_CompareFunc
+  * @type: FT_HashLookup
   *
   * @description:
+  *   handle to a @FT_HashNode pointer. This is returned by
+  *   the @ft_hash_lookup function and can later be used by
+  *   @ft_hash_add or @ft_hash_remove
+  */
+  typedef FT_HashNode*     FT_HashLookup;
+
+
+ /***********************************************************
+  *
+  * @type: FT_Hash_EqualFunc
+  *
+  * @description:
   *   a function used to compare two nodes of the hash table
   *
   * @input:
@@ -72,8 +84,8 @@
   *   1 iff the 'keys' in 'node1' and 'node2' are identical.
   *   0 otherwise.
   */
-  typedef FT_Int  (*FT_Hash_CompareFunc)( const FT_HashNode  node1,
-                                          const FT_HashNode  node2 );
+  typedef FT_Int  (*FT_Hash_EqualFunc)( FT_HashNode  node1,
+                                        FT_HashNode  node2 );
 
 
  /***********************************************************
@@ -106,17 +118,13 @@
   */
   typedef struct FT_HashRec_
   {
-    FT_Memory            memory;
     FT_HashNode*         buckets;
     FT_UInt              p;
     FT_UInt              mask;  /* really maxp-1 */
-    FT_UInt              slack;
+    FT_Long              slack;
+    FT_Hash_EqualFunc    node_equal;
+    FT_Memory            memory;
 
-
-    FT_UInt              node_size;
-    FT_Hash_CompareFunc  node_compare;
-    FT_Hash_ComputeFunc  node_hash;
-
   } FT_HashRec, *FT_Hash;
 
 
@@ -168,36 +176,39 @@
   *   initialize a dynamic hash table
   *
   * @input:
-  *   table   :: handle to target hash table structure
-  *   compare :: node comparison function
-  *   memory  :: memory manager handle used to allocate the
-  *              buckets array within the hash table
+  *   table      :: handle to target hash table structure
+  *   node_equal :: node comparison function
+  *   memory     :: memory manager handle used to allocate the
+  *                 buckets array within the hash table
   *
+  * @return:
+  *   error code. 0 means success
+  *
   * @note:
   *   the node comparison function should only compare node _keys_
   *   and ignore values !! with good hashing computation (which the
   *   user must perform itself), the comparison function should be
-  *   pretty selfom called.
+  *   pretty seldom called.
   *
   *   here is a simple example:
   *
   *   {
-  *     static int my_compare( const MyNode  node1,
-  *                            const MyNode  node2 )
+  *     static int my_equal( MyNode  node1,
+  *                          MyNode  node2 )
   *     {
   *       // compare keys of 'node1' and 'node2'
-  *       return strcmp( node1->key, node2->key );
+  *       return (strcmp( node1->key, node2->key ) == 0);
   *     }
   *
   *     ....
   *
-  *     ft_hash_init( &hash, (FT_Hash_CompareFunc) my_compare, memory );
+  *     ft_hash_init( &hash, (FT_Hash_EqualFunc) my_compare, memory );
   *     ....
   *   }
   */
-  FT_BASE( void )
+  FT_BASE( FT_Error )
   ft_hash_init( FT_Hash              table,
-                FT_Hash_CompareFunc  compare,
+                FT_Hash_EqualFunc  compare,
                 FT_Memory            memory );
 
 
@@ -257,7 +268,7 @@
   *     }
   *   }
   */
-  FT_BASE_DEF( FT_HashNode* )
+  FT_BASE_DEF( FT_HashLookup )
   ft_hash_lookup( FT_Hash      table,
                   FT_HashNode  keynode )
 
@@ -273,10 +284,13 @@
   *
   * @input:
   *   table    :: hash table handle
-  *   pnode    :: pointer-to-hash-node value returned by @ft_hash_lookup
+  *   lookup   :: pointer-to-hash-node value returned by @ft_hash_lookup
   *   new_node :: handle to new hash node. All its fields must be correctly
   *               set, including 'hash'.
   *
+  * @return:
+  *   error code. 0 means success
+  *
   * @note:
   *   this function should always be used _after_ a call to @ft_hash_lookup
   *   that returns a pointer to a NULL  handle. Here's an example:
@@ -308,18 +322,21 @@
   *
   *       // allocate a new node - and set it up
   *       node = (MyNode) malloc( sizeof(*node) );
+  *       if ( node == NULL ) .....
+  *
   *       node->hnode.hash = noderec.hnode.hash;
   *       node->key        = key;
   *       node->value      = value;
   *
   *       // add it to the hash table
-  *       ft_hash_add( table, pnode, node );
+  *       error = ft_hash_add( table, pnode, node );
+  *       if (error) ....
   *     }
   */
-  FT_BASE( void )
-  ft_hash_add( FT_Hash       table,
-               FT_HashNode*  pnode,
-               FT_HashNode   new_node );
+  FT_BASE( FT_Error )
+  ft_hash_add( FT_Hash        table,
+               FT_HashLookup  lookup,
+               FT_HashNode    new_node );
 
 
  /****************************************************************
@@ -332,7 +349,7 @@
   *
   * @input:
   *   table   :: hash table handle
-  *   pnode   :: pointer-to-hash-node value returned by @ft_hash_lookup
+  *   lookup  :: pointer-to-hash-node value returned by @ft_hash_lookup
   *
   * @note:
   *   this function doesn't free the node itself !! Here's an example:
@@ -354,15 +371,16 @@
   *       node  = *pnode;
   *       if ( node != NULL )
   *       {
-  *         ft_hash_remove( table, pnode );
-  *         free( node );
+  *         error = ft_hash_remove( table, pnode );
+  *         if ( !error )
+  *           free( node );
   *       }
   *     }
   *   }
   */
-  FT_BASE( void )
-  ft_hash_remove( FT_Hash       table,
-                  FT_HashNode*  pnode );
+  FT_BASE( FT_Error )
+  ft_hash_remove( FT_Hash        table,
+                  FT_HashLookup  lookup )
 
 
 
--- a/include/freetype/internal/ftobject.h
+++ b/include/freetype/internal/ftobject.h
@@ -140,14 +140,11 @@
   *   object    :: target object handle
   *   init_data :: optional pointer to initialization data
   *
-  * @throws: any
-  *
-  *   the object is _assumed_ to be reachable from the cleanup
-  *   stack when the constructor is called. This means that
-  *   any exception can be thrown safely in it.
+  * @return:
+  *   error code. 0 means success
   */
-  typedef void  (*FT_Object_InitFunc)( FT_Object   object,
-                                       FT_Pointer  init_data );
+  typedef FT_Error  (*FT_Object_InitFunc)( FT_Object   object,
+                                           FT_Pointer  init_data );
 
  /**************************************************************
   *
@@ -158,8 +155,6 @@
   *
   * @input:
   *   object    :: handle to target object
-  *
-  * @throws: *never* !!
   */
   typedef void  (*FT_Object_DoneFunc)( FT_Object   object );
 
@@ -179,6 +174,7 @@
   *               object sub-system.)
   *
   *   magic    :: a 32-bit magic number used for decoding
+  *   super    :: pointer to super class
   *   type     :: the @FT_Type descriptor of this class
   *   memory   :: the current memory manager handle
   *   library  :: the current library handle
@@ -185,6 +181,8 @@
   *   info     :: an opaque pointer to class-specific information
   *               managed by the FreeType object sub-system
   *
+  *   class_done :: the class destructor function
+  *
   *   obj_size :: size of class instances in bytes
   *   obj_init :: class instance constructor
   *   obj_done :: class instance destructor
@@ -193,11 +191,14 @@
   {
     FT_ObjectRec        object;
     FT_UInt32           magic;
+    FT_Class            super;
     FT_Type             type;
     FT_Memory           memory;
     FT_Library          library;
     FT_Pointer          info;
 
+    FT_Object_DoneFunc  class_done;
+
     FT_UInt             obj_size;
     FT_Object_InitFunc  obj_init;
     FT_Object_DoneFunc  obj_done;
@@ -363,80 +364,193 @@
 
  /**************************************************************
   *
-  * @function: ft_object_new
+  * @function: ft_object_create
   *
   * @description:
   *   create a new object (class instance)
   *
+  * @output:
+  *   aobject   :: new object handle. NULL in case of error
+  *
   * @input:
   *   clazz     :: object's class pointer
   *   init_data :: optional pointer to initialization data
   *
   * @return:
-  *   handle to new object. Cannot be NULL !
+  *   error code. 0 means success
   */
-  FT_BASE_DEF( FT_Object )
-  ft_object_new( FT_Class    clazz,
-                 FT_Pointer  init_data );
+  FT_BASE_DEF( FT_Error )
+  ft_object_create( FT_Object  *aobject,
+                    FT_Class    clazz,
+                    FT_Pointer  init_data );
 
 
  /**************************************************************
   *
-  * @function: ft_object_create
+  * @function: ft_object_create_from_type
   *
   * @description:
-  *   a variation of @ft_object_new that should be used when
-  *   creating a new object that is owned by another object
-  *   which is reachable from the cleanup stack.
+  *   create a new object (class instance) from a @FT_Type
   *
-  *   this function is a bit more akward to use but completely
-  *   avoids push/pop pairs during object construction and is
-  *   therefore faster.
-  *
   * @output:
-  *   pobject   :: new object handle
+  *   aobject   :: new object handle. NULL in case of error
   *
   * @input:
-  *   clazz     :: object's class pointer
+  *   type      :: object's type descriptor
   *   init_data :: optional pointer to initialization data
-  *   push      :: boolean. If true, the new object is pushed
-  *                on top of the cleanup stack.
+  *
+  * @return:
+  *   error code. 0 means success
+  *
+  * @note:
+  *   this function is slower than @ft_object_create
+  *
+  *   this is equivalent to calling @ft_class_from_type followed by
+  *   @ft_object_create
   */
-  FT_BASE_DEF( void )
-  ft_object_create( FT_Object  *pobject,
-                    FT_Class    clazz,
-                    FT_Pointer  init_data );
+  FT_BASE_DEF( FT_Error )
+  ft_object_create_from_type( FT_Object  *aobject,
+                              FT_Type     type,
+                              FT_Pointer  init_data,
+                              FT_Library  library );
 
+
+
+ /**************************************************************
+  *
+  * @macro FT_OBJ_CREATE (object,class,init)
+  *
+  * @description:
+  *   a convenient macro used to create new objects. see
+  *   @ft_object_create for details
+  */
+#define  FT_OBJ_CREATE( _obj, _clazz, _init )   \
+               ft_object_create( FT_OBJECT_P(&(_obj)), _clazz, _init )
+
+
+ /**************************************************************
+  *
+  * @macro FT_CREATE (object,class,init)
+  *
+  * @description:
+  *   a convenient macro used to create new objects. It also
+  *   sets an _implicit_ local variable named "error" to the error
+  *   code returned by the object constructor.
+  */
+#define  FT_CREATE( _obj, _clazz, _init )  \
+             FT_MEM_SET( FT_OBJ_CREATE( _obj, _clazz, _init ) )
+
+ /**************************************************************
+  *
+  * @macro FT_OBJ_CREATE_FROM_TYPE (object,type,init)
+  *
+  * @description:
+  *   a convenient macro used to create new objects. see
+  *   @ft_object_create_from_type for details
+  */
+#define  FT_OBJ_CREATE_FROM_TYPE( _obj, _type, _init, _lib )   \
+               ft_object_create_from_type( FT_OBJECT_P(&(_obj)), _type, _init, _lib )
+
+
+ /**************************************************************
+  *
+  * @macro FT_CREATE_FROM_TYPE (object,type,init)
+  *
+  * @description:
+  *   a convenient macro used to create new objects. It also
+  *   sets an _implicit_ local variable named "error" to the error
+  *   code returned by the object constructor.
+  */
+#define  FT_CREATE_FROM_TYPE( _obj, _type, _init, _lib )  \
+             FT_MEM_SET( FT_OBJ_CREATE( _obj, _type, _init, _lib ) )
+
+
  /* */
 
-  FT_BASE_DEF( FT_Class )
-  ft_class_find_by_type( FT_Type    type,
-                         FT_Memory  memory );
+ /**************************************************************
+  *
+  * @function: ft_class_from_type
+  *
+  * @description:
+  *   retrieves the class object corresponding to a given type
+  *   descriptor. The class is created when needed
+  *
+  * @output:
+  *   aclass  :: handle to corresponding class object. NULL in
+  *              case of error
+  *
+  * @input:
+  *   type    :: type descriptor handle
+  *   library :: library handle
+  *
+  * @return:
+  *   error code. 0 means success
+  */
+  FT_BASE_DEF( FT_Error )
+  ft_class_from_type( FT_Class   *aclass,
+                      FT_Type     type,
+                      FT_Library  library );
 
-  FT_BASE_DEF( FT_Class )
-  ft_class_find_by_name( FT_CString  class_name,
-                         FT_Memory   memory );
 
-  FT_BASE_DEF( FT_Object )
-  ft_object_new_from_type( FT_Type     type,
-                           FT_Pointer  data,
-                           FT_Memory   memory );
+ /**************************************************************
+  *
+  * @function: ft_class_from_name
+  *
+  * @description:
+  *   retrieves the class object corresponding to a given type
+  *   name. The class is created when needed
+  *
+  * @output:
+  *   aclass  :: handle to corresponding class object. NULL
+  *              in case of error
+  *
+  * @input:
+  *   name    :: class name
+  *   library :: library handle
+  *
+  * @return:
+  *   error code. 0 means success
+  *
+  * @note:
+  *   this function is _very_ slow. You should only use it for
+  *   debugging purposes..
+  */
+  FT_BASE_DEF( FT_Error )
+  ft_class_from_name( FT_Class   *aclass,
+                      FT_CString  class_name,
+                      FT_Library  library );
+ /* */
 
-  FT_BASE_DEF( void )
-  ft_object_create_from_type( FT_Object  *pobject,
-                              FT_Type     type,
-                              FT_Pointer  init_data,
-                              FT_Memory   memory );
+#include FT_INTERNAL_HASH_H
 
-  FT_BASE_DEF( void )
-  ft_object_push( FT_Object  object );
+  typedef struct FT_ClassHNodeRec_*  FT_ClassHNode;
 
-  FT_BASE_DEF( void )
-  ft_object_pop( FT_Object  object );
+  typedef struct FT_ClassHNodeRec_
+  {
+    FT_HashNodeRec  hnode;
+    FT_Type         type;
+    FT_Class        clazz;
 
+  } FT_ClassHNodeRec;
+
+  typedef struct FT_MetaClassRec_
+  {
+    FT_ClassRec   clazz;         /* the meta-class is a class itself */
+    FT_HashRec    type_to_class; /* the type => class hash table */
+
+  } FT_MetaClassRec, *FT_MetaClass;
+
+
+ /* initialize meta class */
+  FT_BASE_DEF( FT_Error )
+  ft_metaclass_init( FT_MetaClass  meta,
+                     FT_Library    library );
+
+ /* finalize meta class - destroy all registered class objects */
   FT_BASE_DEF( void )
-  ft_object_pop_destroy( FT_Object  object );
+  ft_metaclass_done( FT_MetaClass  meta );
 
+ /* */
 
 FT_END_HEADER
 
--- a/src/autohint/ahglyph.c
+++ b/src/autohint/ahglyph.c
@@ -815,7 +815,7 @@
             segment_dir = point->out_dir;
 
             /* clear all segment fields */
-            FT_MEM_SET( segment, 0, sizeof ( *segment ) );
+            FT_ZERO( segment );
 
             segment->dir      = segment_dir;
             segment->flags    = ah_edge_normal;
@@ -877,7 +877,7 @@
         if ( min_point )
         {
           /* clear all segment fields */
-          FT_MEM_SET( segment, 0, sizeof ( *segment ) );
+          FT_ZERO( segment );
 
           segment->dir   = segment_dir;
           segment->flags = ah_edge_normal;
@@ -893,7 +893,7 @@
         if ( max_point )
         {
           /* clear all segment fields */
-          FT_MEM_SET( segment, 0, sizeof ( *segment ) );
+          FT_ZERO( segment );
 
           segment->dir   = segment_dir;
           segment->flags = ah_edge_normal;
--- a/src/base/fthash.c
+++ b/src/base/fthash.c
@@ -45,7 +45,7 @@
 
 
 
-  FT_BASE_DEF( void )
+  FT_BASE_DEF( FT_Error )
   ft_hash_init( FT_Hash              table,
                 FT_Hash_CompareFunc  compare,
                 FT_Memory            memory )
@@ -196,7 +196,7 @@
         table->mask >>= 1;
         p             = table->mask;
 
-        FT_RENEW_ARRAY( hash->buckets, (mask+1)*2, (mask) );
+        FT_RENEW_ARRAY( hash->buckets, (mask+1)*2, (mask+1) );
       }
       else
         p--;
--- a/src/base/ftstroker.c
+++ b/src/base/ftstroker.c
@@ -5,6 +5,171 @@
  /***************************************************************************/
  /***************************************************************************/
  /*****                                                                 *****/
+ /*****                       BEZIER COMPUTATIONS                       *****/
+ /*****                                                                 *****/
+ /***************************************************************************/
+ /***************************************************************************/
+
+#define FT_SMALL_CONIC_THRESHOLD   (FT_ANGLE_PI/6)
+#define FT_SMALL_CUBIC_THRESHOLD   (FT_ANGLE_PI/6)
+#define FT_EPSILON  2
+
+#define FT_IS_SMALL(x)  ((x) > -FT_EPSILON && (x) < FT_EPSILON)
+
+  static FT_Pos
+  ft_pos_abs( FT_Pos  x )
+  {
+    return  x >= 0 ? x : -x ;
+  }
+
+  static void
+  ft_conic_split( FT_Vector*  base )
+  {
+    FT_Pos  a, b;
+
+
+    base[4].x = base[2].x;
+    b = base[1].x;
+    a = base[3].x = ( base[2].x + b )/2;
+    b = base[1].x = ( base[0].x + b )/2;
+    base[2].x = ( a + b )/2;
+
+    base[4].y = base[2].y;
+    b = base[1].y;
+    a = base[3].y = ( base[2].y + b )/2;
+    b = base[1].y = ( base[0].y + b )/2;
+    base[2].y = ( a + b )/2;
+  }
+
+
+  static FT_Bool
+  ft_conic_is_small_enough( FT_Vector*  base,
+                            FT_Angle   *angle_in,
+                            FT_Angle   *angle_out )
+  {
+    FT_Vector  d1, d2;
+    FT_Angle   theta;
+    FT_Bool    close1, close2;
+
+    d1.x = base[1].x - base[2].x;
+    d1.y = base[1].y - base[2].y;
+    d2.x = base[0].x - base[1].x;
+    d2.y = base[0].y - base[1].y;
+
+    close1 = FT_IS_SMALL(d1.x) && FT_IS_SMALL(d1.y);
+    close2 = FT_IS_SMALL(d2.x) && FT_IS_SMALL(d2.y);
+
+    if (close1)
+    {
+      if (close2)
+        *angle_in = *angle_out = 0;
+      else
+        *angle_in = *angle_out = FT_Vector_Angle( &d2 );
+    }
+    else if (close2)
+    {
+      *angle_in = *angle_out = FT_Vector_Angle( &d1 );
+    }
+    else
+    {
+      *angle_in  = FT_Vector_Angle(&d1);
+      *angle_out = FT_Vector_Angle(&d2);
+    }
+
+    theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
+    return ( theta < FT_SMALL_CONIC_THRESHOLD );
+  }
+
+
+  static void
+  ft_cubic_split( FT_Vector*  base )
+  {
+    FT_Pos  a, b, c, d;
+
+
+    base[6].x = base[3].x;
+    c = base[1].x;
+    d = base[2].x;
+    base[1].x = a = ( base[0].x + c )/2;
+    base[5].x = b = ( base[3].x + d )/2;
+    c = ( c + d )/2;
+    base[2].x = a = ( a + c )/2;
+    base[4].x = b = ( b + c )/2;
+    base[3].x = ( a + b )/2;
+
+    base[6].y = base[3].y;
+    c = base[1].y;
+    d = base[2].y;
+    base[1].y = a = ( base[0].y + c )/2;
+    base[5].y = b = ( base[3].y + d )/2;
+    c = ( c + d )/2;
+    base[2].y = a = ( a + c )/2;
+    base[4].y = b = ( b + c )/2;
+    base[3].y = ( a + b )/2;
+  }
+
+
+  static FT_Bool
+  ft_cubic_is_small_enough( FT_Vector*  base,
+                            FT_Angle   *angle_in,
+                            FT_Angle   *angle_mid,
+                            FT_Angle   *angle_out )
+  {
+    FT_Vector  d1, d2, d3;
+    FT_Angle   theta1, theta2;
+    FT_Bool    close1, close2, close3;
+
+    d1.x = base[2].x - base[3].x;
+    d1.y = base[2].y - base[3].y;
+    d2.x = base[1].x - base[2].x;
+    d2.y = base[1].y - base[2].y;
+    d3.x = base[0].x - base[1].x;
+    d3.y = base[0].y - base[1].y;
+
+    close1 = FT_IS_SMALL(d1.x) && FT_IS_SMALL(d1.y);
+    close2 = FT_IS_SMALL(d2.x) && FT_IS_SMALL(d2.y);
+    close3 = FT_IS_SMALL(d3.x) && FT_IS_SMALL(d3.y);
+
+    if (close1 || close3)
+    {
+      if (close2)
+      {
+        /* basically a point */
+        *angle_in = *angle_out = *angle_mid = 0;
+      }
+      else if (close1)
+      {
+        *angle_in  = *angle_mid = FT_Vector_Angle( &d2 );
+        *angle_out = FT_Vector_Angle( &d3 );
+      }
+      else  /* close2 */
+      {
+        *angle_in  = FT_Vector_Angle( &d1 );
+        *angle_mid = *angle_out = FT_Vector_Angle( &d2 );
+      }
+    }
+    else if (close2)
+    {
+      *angle_in  = *angle_mid = FT_Vector_Angle( &d1 );
+      *angle_out = FT_Vector_Angle( &d3 );
+    }
+    else
+    {
+      *angle_in  = FT_Vector_Angle(&d1);
+      *angle_mid = FT_Vector_Angle(&d2);
+      *angle_out = FT_Vector_Angle(&d3);
+    }
+    theta1 = ft_pos_abs( nv_angle_diff( *angle_in,  *angle_mid ) );
+    theta2 = ft_pos_abs( nv_angle_diff( *angle_mid, *angle_out ) );
+    return ( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
+             theta2 < FT_SMALL_CUBIC_THRESHOLD );
+  }
+
+
+
+ /***************************************************************************/
+ /***************************************************************************/
+ /*****                                                                 *****/
  /*****                       STROKE BORDERS                            *****/
  /*****                                                                 *****/
  /***************************************************************************/
@@ -16,7 +181,7 @@
     FT_STROKE_TAG_CUBIC = 2,   /* cubic off-point */
     FT_STROKE_TAG_BEGIN = 4,   /* sub-path start  */
     FT_STROKE_TAG_END   = 8    /* sub-path end    */
-  
+
   } FT_StrokeTags;
 
 
@@ -29,7 +194,7 @@
     FT_Bool     movable;
     FT_Int      start;    /* index of current sub-path start point */
     FT_Memory   memory;
-  
+
   } FT_StrokeBorderRec, *FT_StrokeBorder;
 
 
@@ -40,12 +205,12 @@
     FT_UInt   old_max = border->max_points;
     FT_UInt   new_max = border->num_points + new_points;
     FT_Error  error   = 0;
-    
+
     if ( new_max > old_max )
     {
       FT_UInt    cur_max = old_max;
-      FT_Memory  memory  = 
-      
+      FT_Memory  memory  =
+
       while ( cur_max < new_max )
         cur_max += (cur_max >> 1) + 16;
 
@@ -52,7 +217,7 @@
       if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
            FT_RENEW_ARRAY( border->tags,   old_max, cur_max ) )
         goto Exit;
-      
+
       border->max_points = cur_max;
     }
   Exit:
@@ -63,35 +228,47 @@
   ft_stroke_border_close( FT_StrokeBorder  border )
   {
     FT_ASSERT( border->start >= 0 );
-    
+
     border->tags[ border->start        ] |= FT_STROKE_TAG_BEGIN;
     border->tags[ border->num_points-1 ] |= FT_STROKE_TAG_END;
-    
-    border->start = -1;
+
+    border->start   = -1;
+    border->movable = 0;
   }
 
 
   static FT_Error
   ft_stroke_border_lineto( FT_StrokeBorder  border,
-                           FT_Vector*       to )
+                           FT_Vector*       to,
+                           FT_Bool          movable )
   {
     FT_Error  error;
-    
+
     FT_ASSERT( border->start >= 0 );
-    
-    error = ft_stroker_border_grow( border, 1 );
-    if (!error)
+
+    if ( border->movable )
     {
-      FT_Vector*  vec = border->points + border->num_points;
-      FT_Byte*    tag = border->tags   + border->num_points;
-      
-      vec[0] = *to;
-      tag[0] = FT_STROKE_TAG_ON;
-      
-      border->num_points += 1;
+      /* move last point */
+      border->points[ border->num_points-1 ] = *to;
     }
+    else
+    {
+      /* add one point */
+      error = ft_stroke_border_grow( border, 1 );
+      if (!error)
+      {
+        FT_Vector*  vec = border->points + border->num_points;
+        FT_Byte*    tag = border->tags   + border->num_points;
+
+        vec[0] = *to;
+        tag[0] = FT_STROKE_TAG_ON;
+
+        border->num_points += 1;
+      }
+    }
+    border->movable = movable;
     return error;
-  }                           
+  }
 
 
   static FT_Error
@@ -100,25 +277,26 @@
                             FT_Vector*       to )
   {
     FT_Error  error;
-    
+
     FT_ASSERT( border->start >= 0 );
-    
-    error = ft_stroker_border_grow( border, 2 );
+
+    error = ft_stroke_border_grow( border, 2 );
     if (!error)
     {
       FT_Vector*  vec = border->points + border->num_points;
       FT_Byte*    tag = border->tags   + border->num_points;
-      
+
       vec[0] = *control;
       vec[1] = *to;
 
       tag[0] = 0;
       tag[1] = FT_STROKE_TAG_ON;
-      
+
       border->num_points += 2;
     }
+    border->movable = 0;
     return error;
-  }                           
+  }
 
 
   static FT_Error
@@ -128,45 +306,117 @@
                             FT_Vector*       to )
   {
     FT_Error  error;
-    
+
     FT_ASSERT( border->start >= 0 );
-    
-    error = ft_stroker_border_grow( border, 3 );
+
+    error = ft_stroke_border_grow( border, 3 );
     if (!error)
     {
       FT_Vector*  vec = border->points + border->num_points;
       FT_Byte*    tag = border->tags   + border->num_points;
-      
+
       vec[0] = *control1;
       vec[1] = *control2;
       vec[2] = *to;
-      
+
       tag[0] = FT_STROKE_TAG_CUBIC;
       tag[1] = FT_STROKE_TAG_CUBIC;
       tag[2] = FT_STROKE_TAG_ON;
-      
+
       border->num_points += 3;
     }
+    border->movable = 0;
     return error;
   }
 
 
+#define  FT_ARC_CUBIC_ANGLE  (FT_ANGLE_PI/2)
+
+
   static FT_Error
+  ft_stroke_border_arcto( FT_StrokerBorder  border,
+                           FT_Vector*        center,
+                           FT_Fixed          radius,
+                           FT_Angle          angle_start,
+                           FT_Angle          angle_diff )
+  {
+    FT_Angle   total, angle, step, rotate, next, theta;
+    FT_Vector  a, b, a2, b2;
+    FT_Fixed   length;
+    FT_Error   error = 0;
+
+    /* compute start point */
+    FT_Vector_From_Polar( &a, radius, angle_start );
+    a.x += center.x;
+    a.y += center.y;
+
+    total  = angle_diff;
+    angle  = angle_start;
+    rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
+
+    while (total != 0)
+    {
+      step = total;
+      if ( step > FT_ARC_CUBIC_ANGLE )
+        step   = FT_ARC_CUBIC_ANGLE;
+
+      else if ( step < -FT_ARC_CUBIC_ANGLE )
+        step = -FT_ARC_CUBIC_ANGLE;
+
+      next  = angle + step;
+      theta = step;
+      if ( theta < 0 )
+        theta = -theta;
+
+      theta >>= 1
+
+      /* compute end point */
+      FT_Vector_From_Polar( &b, radius, next );
+      b.x += center->x;
+      b.y += center->y;
+
+      /* compute first and second control points */
+      length = FT_MulDiv( radius, FT_Sin(theta)*4,
+                          (FT_ONE + FT_Cos(theta))*3 );
+
+      FT_Vector_From_Polar( &a2, length, angle + rotate );
+      a2.x += a.x;
+      a2.y += a.y;
+
+      FT_Vector_From_Polar( &b2, length, next - rotate );
+      b2.x += b.x;
+      b2.y += b.y;
+
+      /* add cubic arc */
+      error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
+      if (error) break;
+
+      /* process the rest of the arc ?? */
+      a      = b;
+      total -= step;
+      angle  = next;
+    }
+    return error;
+  }
+
+
+  static FT_Error
   ft_stroke_border_moveto( FT_StrokeBorder  border,
                            FT_Vector*       to )
   {
     FT_Error  error;
-    
+
     /* close current open path if any ? */
     if ( border->start >= 0 )
       ft_stroke_border_close( border );
-    
-    border->start = border->num_points;
 
-    return ft_stroke_border_lineto( border, to );
+    border->start   = border->num_points;
+    border->movable = 0;
+
+    return ft_stroke_border_lineto( border, to, 0 );
   }
-  
- 
+
+
  static void
  ft_stroke_border_init( FT_StrokeBorder  border,
                         FT_Memory        memory )
@@ -174,13 +424,13 @@
    border->memory = memory;
    border->points = NULL;
    border->tags   = NULL;
-   
+
    border->num_points = 0;
    border->max_points = 0;
    border->start      = -1;
  }
- 
 
+
  static void
  ft_stroke_border_reset( FT_StrokeBorder  border )
  {
@@ -187,16 +437,16 @@
    border->num_points = 0;
    border->start      = -1;
  }
- 
 
+
  static void
  ft_stroke_border_done( FT_StrokeBorder  border )
  {
    memory = border->memory;
-   
+
    FT_FREE( border->points );
    FT_FREE( border->tags );
-   
+
    border->num_points = 0;
    border->max_points = 0;
    border->start      = -1;
@@ -211,7 +461,7 @@
  /***************************************************************************/
  /***************************************************************************/
 
-#define  FT_SIDE_TO_ROTATE(s)   (FT_PI2 - (s)*FT_PI)
+#define  FT_SIDE_TO_ROTATE(s)   (FT_ANGLE_PI2 - (s)*FT_ANGLE_PI)
 
   typedef struct FT_StrokerRec_
   {
@@ -230,7 +480,7 @@
 
     FT_StrokeBorderRec   borders[2];
     FT_Memory            memory;
-  
+
   } FT_StrokerRec;
 
 
@@ -240,11 +490,11 @@
   {
     FT_Error    error;
     FT_Stroker  stroker;
-    
+
     if ( !FT_NEW( stroker ) )
     {
       stroker->memory = memory;
-      
+
       ft_stroke_border_init( &stroker->borders[0], memory );
       ft_stroke_border_init( &stroker->borders[1], memory );
     }
@@ -264,27 +514,707 @@
     stroker->line_cap    = line_cap;
     stroker->line_join   = line_join;
     stroker->miter_limit = miter_limit;
-    
+
     ft_stroke_border_reset( &stroker->borders[0] );
     ft_stroke_border_reset( &stroker->borders[1] );
   }
 
 
-  FT_EXPORT( void )
+  FT_EXPORT_DEF( void )
   FT_Stroker_Done( FT_Stroker  stroker )
   {
     if ( stroker )
     {
       FT_Memory  memory = stroker->memory;
-      
+
       ft_stroke_border_done( &stroker->borders[0] );
       ft_stroke_border_done( &stroker->borders[1] );
-      
+
       stroker->memory = NULL;
       FT_FREE( stroker );
     }
   }
 
+
+
+ /* creates a circular arc at a corner or cap */
+  static FT_Error
+  ft_stroker_arcto( FT_Stroker  stroker,
+                    FT_Int      side )
+  {
+    FT_Angle          total, rotate;
+    FT_Fixed          radius = stroker->radius;
+    FT_Error          error  = 0;
+    FT_StrokeBorder*  border = stroker->borders + side;
+
+    rotate = FT_SIDE_TO_ROTATE(side);
+
+    total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+    if (total == FT_ANGLE_PI)
+      total = -rotate*2;
+
+    error = ft_stroke_border_arcto( border,
+                                    &stroker->center,
+                                    radius,
+                                    stroker->angle_in + rotate,
+                                    total );
+    border->movable = 0;
+    return error;
+  }
+
+
+  /* adds a cap at the end of an opened path */
+  static FT_Error
+  ft_stroker_cap( FT_Stroker  stroker,
+                  FT_Angle    angle,
+                  FT_Int      side )
+  {
+    FT_Error  error  = 0;
+
+    if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
+    {
+      /* add a round cap */
+      stroker->angle_in  = angle;
+      stroker->angle_out = angle + FT_ANGLE_PI;
+      error = ft_stroker_arcto( stroker, side );
+    }
+    else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
+    {
+      /* add a square cap */
+      FT_Vector         delta, delta2;
+      FT_Angle          rotate = FT_SIDE_TO_ROTATE(side);
+      FT_Fixed          radius = stroker->radius;
+      FT_StrokeBorder   border = stroker->borders + side;
+
+      FT_Vector_From_Polar( &delta2, radius, angle+rotate );
+      FT_Vector_From_Polar( &delta,  radius, angle );
+
+      delta.x += stroker->center.x + delta2.x;
+      delta.y += stroker->center.y + delta2.y;
+
+      error = ft_stroke_border_lineto( border, &delta, 0 );
+      if (error) goto Exit;
+
+      FT_Vector_From_Polar( &delta2, radius, angle-rotate );
+      FT_Vector_From_Polar( &delta,  radius, angle );
+
+      delta.x += delta2.x + stroker->center.x;
+      delta.y += delta2.y + stroker->center.y;
+
+      error = ft_stroke_border_lineto( border, &delta, 0 );
+    }
+  Exit:
+    return error;
+  }
+
+
+
+ /* process an inside corner, i.e. compute intersection */
+  static FT_Error
+  ft_stroker_inside( FT_Stroker  stroker,
+                     FT_Int      side)
+  {
+    FT_StrokeBorder*  border = stroker->borders + side;
+    FT_Angle          phi, theta, rotate;
+    FT_Fixed          length, thcos, sigma;
+    FT_Vector         delta;
+    FT_Error          error = 0;
+
+
+    rotate = FT_SIDE_TO_ROTATE(side);
+
+    /* compute median angle */
+    theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+    if ( theta == FT_ANGLE_PI )
+      theta = rotate;
+    else
+      theta = theta/2;
+
+    phi = stroker->angle_in + theta;
+
+    thcos  = FT_Cos( theta );
+    sigma  = FT_MulFix( stroker->miter_limit, thcos );
+
+    if ( sigma < 0x10000L )
+    {
+      FT_Vector_From_Polar( &delta, stroker->radius, stroker->angle_out + rotate );
+      delta.x += stroker->center.x;
+      delta.y += stroker->center.y;
+      border->movable = 0;
+    }
+    else
+    {
+      length = FT_DivFix( stroker->radius, thcos );
+
+      FT_Vector_From_Polar( &delta, length, phi + rotate );
+      delta.x += stroker->center.x;
+      delta.y += stroker->center.y;
+    }
+
+    error = ft_stroke_border_lineto( border, &delta, 0 );
+
+  Exit:
+    return error;
+  }
+
+
+ /* process an outside corner, i.e. compute bevel/miter/round */
+  static FT_Error
+  ft_stroker_outside( FT_Stroker  stroker,
+                      FT_Int      side )
+  {
+    FT_StrokeBorder*  border = stroker->borders + side;
+    FT_Error          error;
+    FT_Angle          rotate;
+
+    if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
+    {
+      error = ft_stroker_arcto( stroker, side );
+    }
+    else
+    {
+      /* this is a mitered or beveled corner */
+      FT_Fixed   sigma, radius = stroker->radius;
+      FT_Angle   theta, phi;
+      FT_Fixed   thcos;
+      FT_Bool    miter;
+
+      rotate = FT_SIDE_TO_ROTATE(side);
+      miter  = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
+
+      theta  = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+      if (theta == FT_ANGLE_PI)
+        theta = rotate;
+      else
+        theta = theta/2;
+
+      thcos  = FT_Cos( theta );
+      sigma  = FT_MulFix( stroker->miter_limit, thcos );
+
+      if ( sigma >= FT_ONE )
+        miter = 0;
+
+      phi = stroker->angle_in + theta + rotate;
+
+      if (miter)  /* this is a miter (broken angle) */
+      {
+        FT_Vector  middle, delta;
+        FT_Fixed  length;
+
+        /* compute middle point */
+        FT_Vector_From_Polar( &middle, FT_MulFix( radius, stroker->miter_limit ),
+                              phi );
+        middle.x += stroker->center.x;
+        middle.y += stroker->center.y;
+
+        /* compute first angle point */
+        length = FT_MulDiv( radius, FT_DivFix( FT_ONE - sigma,
+                                    ft_abs( FT_Sin( theta ) ) );
+
+        FT_Vector_From_Polar( &delta, length, phi + rotate );
+        delta.x += middle.x;
+        delta.y += middle.y;
+
+        error = ft_stroke_border_lineto( border, &delta, 0 );
+        if (error) goto Exit;
+
+        /* compute second angle point */
+        FT_Vector_From_Polar( &delta, length, phi - rotate );
+        delta.x += middle.x;
+        delta.y += middle.y;
+
+        error = ft_stroke_border_lineto( border, &delta, 0 );
+        if (error) goto Exit;
+
+        /* finally, add a movable end point */
+        FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
+        delta.x += stroker->center.x;
+        delta.y += stroker->center.y;
+
+        error = ft_stroke_border_lineto( border, &delta, 1 );
+      }
+      else /* this is a bevel (intersection) */
+      {
+        FT_Fixed  length;
+        FT_Vector  delta;
+
+        length = FT_DivFix( stroker->radius, thcos );
+
+        FT_Vector_From_Polar( &delta, length, phi );
+        delta.x += stroker->center.x;
+        delta.y += stroker->center.y;
+
+        error = ft_stroke_border_lineto( border, &delta, 0 );
+        if (error) goto Exit;
+
+        /* now add end point */
+        FT_Vector_From_Polar( &delta, stroker->radius, stroker->angle_out + rotate );
+        delta.x += stroker->center.x;
+        delta.y += stroker->center.y;
+
+        error = ft_stroke_border_lineto( border, &delta, 1 );
+      }
+    }
+  Exit:
+    return error;
+  }
+
+
+  static FT_Error
+  ft_stroker_process_corner( FT_Stroker  stroker )
+  {
+    FT_Error  error = 0;
+    FT_Angle  turn;
+    FT_Int    inside_side;
+
+    turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+
+    /* no specific corner processing is required if the turn is 0 */
+    if (turn == 0)
+      goto Exit;
+
+    /* when we turn to the right, the inside side is 0 */
+    inside_side = 0;
+
+    /* otherwise, the inside side is 1 */
+    if (turn < 0)
+      inside_side = 1;
+
+    /* process the inside side */
+    error = ft_stroker_inside( stroker, inside_side );
+    if (error) goto Exit;
+
+    /* process the outside side */
+    error = ft_stroker_outside( stroker, 1-inside_side );
+
+  Exit:
+    return error;
+  }
+
+
+ /* add two points to the left and right borders corresponding to the */
+ /* start of the subpath..                                            */
+  static FT_Error
+  ft_stroker_subpath_start( FT_Stroker  stroker,
+                            FT_Angle    start_angle )
+  {
+    FT_Vector        delta;
+    FT_Vector        point;
+    FT_Error         error;
+    FT_StrokeBorder  border;
+
+    FT_Vector_From_Polar( &delta, stroker->radius, start_angle + FT_ANGLE_PI2 );
+
+    point.x = stroker->center.x + delta.x;
+    point.y = stroker->center.y + delta.y;
+
+    border = stroker->borders;
+    error = ft_stroke_border_moveto( border, &point, stroker->subpath_open );
+    if (error) goto Exit;
+
+    point.x = stroker->center.x - delta.x;
+    point.y = stroker->center.y - delta.y;
+
+    border++;
+    error = ft_stroke_border_moveto( border, &point, stroker->subpath_open );
+
+    /* save angle for last cap */
+    stroker->subpath_angle = start_angle;
+    stroker->first_point   = 0;
+
+  Exit:
+    return error;
+  }
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stroker_LineTo( FT_Stroker  stroker,
+                     FT_Vector*  to )
+  {
+    FT_Error          error = 0;
+    FT_StrokeBorder*  border;
+    FT_Vector         delta;
+    FT_Angle          angle;
+    FT_Int            side;
+
+    delta.x = to->x - stroker->center.x;
+    delta.y = to->y - stroker->center.y;
+
+    angle = FT_Atan2( delta.x, delta.y );
+    FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
+
+    /* process corner if necessary */
+    if ( stroker->first_point )
+    {
+      /* this is the first segment of a subpath. We need to      */
+      /* add a point to each border at their respective starting */
+      /* point locations..                                       */
+      error = ft_stroker_subpath_start( stroker, angle );
+      if (error) goto Exit;
+    }
+    else
+    {
+      /* process the current corner */
+      stroker->angle_out = angle;
+      error = ft_stroker_process_corner( stroker );
+      if (error) goto Exit;
+    }
+
+    /* now add a line segment to both the "inside" and "outside" paths */
+
+    for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
+    {
+      FT_Vector  point;
+
+      point.x = to->x + delta.x;
+      point.y = to->y + delta.y;
+
+      error = ft_stroke_border_lineto( border, &point, 1 );
+      if (error) goto Exit;
+
+      delta.x = -delta.x;
+      delta.y = -delta.y;
+    }
+
+    stroker->angle_in  = angle;
+    stroker->center    = *to;
+
+  Exit:
+    return error;
+  }
+
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stroker_ConicTo( FT_Stroker  stroker,
+                      FT_Vector*  control,
+                      FT_Vector*  to )
+  {
+    FT_Error    error = 0;
+    FT_Vector   bez_stack[34];
+    FT_Vector*  arc;
+    FT_Vector*  limit = bez_stack + 30;
+    FT_Angle    start_angle;
+    FT_Bool     first_arc = 1;
+
+    arc    = bez_stack;
+    arc[0] = *to;
+    arc[1] = *control;
+    arc[2] = stroker->center;
+
+    while ( arc >= bez_stack )
+    {
+      FT_Angle  angle_in, angle_out;
+
+      if ( arc < limit &&
+           !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
+      {
+        ft_conic_split( arc );
+        arc += 2;
+        continue;
+      }
+
+      if ( first_arc )
+      {
+        first_arc = 0;
+
+        start_angle = angle_in;
+
+        /* process corner if necessary */
+        if ( stroker->first_point )
+          error = ft_stroker_subpath_start( stroker, start_angle );
+        else
+        {
+          stroker->angle_out = start_angle;
+          error = ft_stroker_process_corner( stroker );
+        }
+      }
+
+      /* the arc's angle is small enough, we can add it directly to each */
+      /* border..                                                        */
+      {
+        FT_Vector  ctrl, end;
+        FT_Angle   theta, phi, rotate;
+        FT_Fixed  length;
+        FT_Int     side;
+
+        theta  = FT_Angle_Diff( angle_in, angle_out )/2;
+        phi    = angle_in + theta;
+        length = FT_DivFix( stroker->radius, FT_Cos(theta) );
+
+        for ( side = 0; side <= 1; side++ )
+        {
+          rotate = FT_SIDE_TO_ROTATE(side);
+
+          /* compute control point */
+          FT_Vector_From_Polar( &ctrl, length, phi + rotate );
+          ctrl.x += arc[1].x;
+          ctrl.y += arc[1].y;
+
+          /* compute end point */
+          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+          end.x += arc[0].x;
+          end.y += arc[0].y;
+
+          error = ft_stroke_border_conicto( stroker->borders + side, &ctrl, &end );
+          if (error) goto Exit;
+        }
+      }
+
+      arc -= 2;
+
+      if (arc < bez_stack)
+        stroker->angle_in = angle_out;
+    }
+
+    stroker->center = *to;
+
+  Exit:
+    return error;
+  }
+
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stroker_CubicTo( FT_Stroker  stroker,
+                      FT_Vector*      control1,
+                      FT_Vector*      control2,
+                      FT_Vector*      to )
+  {
+    FT_Error    error = 0;
+    FT_Vector   bez_stack[37];
+    FT_Vector*  arc;
+    FT_Vector*  limit = bez_stack + 32;
+    FT_Angle    start_angle;
+    FT_Bool     first_arc = 1;
+
+    arc    = bez_stack;
+    arc[0] = *to;
+    arc[1] = *control2;
+    arc[2] = *control1;
+    arc[3] = stroker->center;
+
+    while ( arc >= bez_stack )
+    {
+      FT_Angle  angle_in, angle_mid, angle_out;
+
+      if ( arc < limit &&
+           !ft_cubic_is_small_enough( arc, &angle_in, &angle_mid, &angle_out ) )
+      {
+        ft_cubic_split( arc );
+        arc += 3;
+        continue;
+      }
+
+      if ( first_arc )
+      {
+        first_arc = 0;
+
+        /* process corner if necessary */
+        start_angle = angle_in;
+
+        if ( stroker->first_point )
+          error = ft_stroker_subpath_start( stroker, start_angle );
+        else
+            {
+              stroker->angle_out = start_angle;
+              error = ft_stroker_process_corner( stroker );
+            }
+        if (error) goto Exit;
+      }
+
+      /* the arc's angle is small enough, we can add it directly to each */
+      /* border..                                                        */
+      {
+        FT_Vector  ctrl1, ctrl2, end;
+        FT_Angle   theta1, phi1, theta2, phi2, rotate;
+        FT_Fixed   length1, length2;
+        FT_Int     side;
+
+        theta1  = ft_pos_abs( angle_mid - angle_in )/2;
+        theta2  = ft_pos_abs( angle_out - angle_mid )/2;
+        phi1    = (angle_mid+angle_in)/2;
+        phi2    = (angle_mid+angle_out)/2;
+        length1 = FT_DivFix( stroker->radius, FT_Cos(theta1) );
+        length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
+
+        for ( side = 0; side <= 1; side++ )
+        {
+          rotate = FT_SIDE_TO_ROTATE(side);
+
+          /* compute control points */
+          FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
+          ft_vector_add( &ctrl1, &arc[2] );
+
+          FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
+          ft_vector_add( &ctrl2, &arc[1] );
+
+          /* compute end point */
+          FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+          ft_vector_add( &end, &arc[0] );
+
+          error = ft_stroke_border_cubicto( stroker->borders + side, &ctrl1, &ctrl2, &end );
+          if (error) goto Exit;
+        }
+      }
+
+      arc -= 3;
+      if (arc < bez_stack)
+        stroker->angle_in = angle_out;
+    }
+
+    stroker->center = *to;
+
+  Exit:
+    return error;
+  }
+
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stroker_BeginSubPath( FT_Stroker  stroker,
+                           FT_Vector*  to,
+                           FT_Bool     open )
+  {
+    /* we cannot process the first point, because there is not enough     */
+    /* information regarding its corner/cap. The latter will be processed */
+    /* in the "end_subpath" routine                                       */
+    /*                                                                    */
+    stroker->first_point   = 1;
+    stroker->center        = *to;
+    stroker->subpath_open  = open;
+
+    /* record the subpath start point index for each border */
+    stroker->subpath_start = *to;
+    return 0;
+  }
+
+
+  static
+  FT_Error  ft_stroker_add_reverse_left( FT_Stroker  stroker,
+                                         FT_Bool     open )
+  {
+    FT_StrokeBorder  right  = stroker->borders + 0;
+    FT_StrokeBorder  left   = stroker->borders + 1;
+    FT_Int           new_points;
+    FT_Error         error  = 0;
+
+    FT_ASSERT( left->start >= 0 );
+
+    new_points = left->num_points - left->start;
+    if ( new_points > 0 )
+    {
+      error = ft_stroker_border_grow( right, (FT_UInt)new_points );
+      if (error) goto Exit;
+      {
+        FT_Vector*    dst_point = right->points + right->num_points;
+        FT_Path_Tag*  dst_tag   = right->tags   + right->num_points;
+        FT_Vector*    src_point = left->points  + left->num_points - 1;
+        FT_Path_Tag*  src_tag   = left->tags    + left->num_points - 1;
+
+        while ( src_point >= left->points + left->start )
+        {
+          *dst_point = *src_point;
+          *dst_tag   = *src_tag;
+
+          if (open)
+            dst_tag[0] &= ~(FT_STROKER_TAG_BEGIN | FT_STROKER_TAG_END);
+          else
+          {
+            /* switch begin/end tags if necessary.. */
+            if (dst_tag[0] & (FT_STROKER_TAG_BEGIN | FT_STROKER_TAG_END))
+              dst_tag[0] ^= (FT_STROKER_TAG_BEGIN | FT_STROKER_TAG_END);
+          }
+
+          src_point--;
+          src_tag--;
+          dst_point++;
+          dst_tag++;
+        }
+      }
+      left->num_points   = left->start;
+      right->num_points += new_points;
+
+      right->movable = 0;
+      left->movable  = 0;
+    }
+  Exit:
+    return error;
+  }
+
+
+ /* there's a lot of magic in this function !! */
+  FT_EXPORT_DEF( FT_Error )
+  FT_Stroker_EndSubPath( FT_Stroker  stroker )
+  {
+    FT_Error  error  = 0;
+
+    if ( stroker->subpath_open )
+    {
+      FT_StrokeBorder  right = stroker->borders;
+
+      /* all right, this is an opened path, we need to add a cap between     */
+      /* right & left, add the reverse of left, then add a final cap between */
+      /* left & right..                                                      */
+      error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
+      if (error) goto Exit;
+
+      /* add reversed points from "left" to "right" */
+      error = ft_stroker_add_reverse_left( stroker, 1 );
+      if (error) goto Exit;
+
+      /* now add the final cap */
+      stroker->center = stroker->subpath_start;
+      error = ft_stroker_cap( stroker, stroker->subpath_angle+FT_ANGLE_PI, 0 );
+      if (error) goto Exit;
+
+      /* now, end the right subpath accordingly. the left one is */
+      /* rewind and doesn't need further processing..            */
+      error = ft_stroke_border_close( right );
+    }
+    else
+    {
+      FT_Angle           turn;
+      FT_Int             inside_side;
+
+      /* process the corner ... */
+      stroker->angle_out = stroker->subpath_angle;
+      turn               = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+
+      /* no specific corner processing is required if the turn is 0 */
+      if (turn != 0)
+      {
+        /* when we turn to the right, the inside side is 0 */
+        inside_side = 0;
+
+        /* otherwise, the inside side is 1 */
+        if (turn < 0)
+          inside_side = 1;
+
+        /* IMPORTANT: WE DO NOT PROCESS THE INSIDE BORDER HERE !! */
+        /* process the inside side */
+        /* error = ft_stroker_inside( stroker, inside_side );
+             if (error) goto Exit; */
+
+        /* process the outside side */
+        error = ft_stroker_outside( stroker, 1-inside_side );
+        if (error) goto Exit;
+      }
+
+      /* we will first end our two subpaths */
+      error = ft_stroker_border_close( stroker->borders + 0 );
+      if (error) goto Exit;
+
+      error = ft_stroker_border_close( stroker->borders + 1 );
+      if (error) goto Exit;
+
+      /* now, add the reversed left subpath to "right" */
+      error = ft_stroker_add_reverse_left( stroker, 0 );
+      if (error) goto Exit;
+    }
+
+  Exit:
+    return error;
+  }
 
 
   
\ No newline at end of file
--- a/src/base/fttrigon.c
+++ b/src/base/fttrigon.c
@@ -453,19 +453,33 @@
 
   /* documentation is in fttrigon.h */
 
+  FT_EXPORT_DEF( void )
+  FT_Vector_From_Polar( FT_Vector*  vec,
+                        FT_Fixed    length,
+                        FT_Angle    angle )
+  {
+    vec->x = length;
+    vec->y = 0;
+
+    FT_Vector_Rotate( vec, angle );
+  }
+
+
+  /* documentation is in fttrigon.h */
+
   FT_EXPORT_DEF( FT_Angle )
   FT_Angle_Dif( FT_Angle  angle1,
                 FT_Angle  angle2 )
   {
     FT_Angle  delta = angle2 - angle1;
-    
+
     delta %= FT_ANGLE_2PI;
 
     if ( delta > FT_ANGLE_PI )
       delta -= FT_ANGLE_2PI;
-    
+
     return delta;
-  }                
+  }
 
 
 /* END */