/***
**
**   Module: ttprog
**
**   Description:
**      This is a module of the T1 to TT font converter. This is a
**      sub-module of Hint module. This modules deals with the 
**      the font program fo the font.
**
**   Author: Michael Jansson
**
**   Created: 8/24/93
**
***/


/**** INCLUDES */
/* General types and definitions. */
#include <limits.h>

/* Special types and definitions. */
#include "titott.h"
#include "types.h"
#include "safemem.h"
#include "metrics.h"
#include "t1msg.h"

/* Module dependent types and prototypes. */
/*#include "hints.h"*/
#include "ttprog.h"



/***** MACROS */
/*-none-*/



/***** CONSTANTS */
#define MAXIP			100

#define CURVEPHASE      6

#define MAXTHINPNTS     512

#define UNDEF        -1

#define SMALL_LOWER     21L
#define SMALL_UPPER     50L
#define LARGE_LOWER     21L
#define LARGE_UPPER     21L

#define BUFSIZE         20

#define TTFUN_SET_ZONE           1 
#define TTFUN_COPY_ZONE          2 
#define TTFUN_STEM_SNAP_WIDTH    3 
#define TTFUN_STEM_STD_WIDTH     4 
#define TTFUN_SHIFT_BLUE_ZONE    5 
#define TTFUN_ALIGN_BLUE_ZONE    6 
#define TTFUN_COPY_FAMILY        7 
#define TTFUN_WRITE_STEM         8 
#define TTFUN_VERTICAL           9 
#define TTFUN_HORIZONTAL         10
#define TTFUN_VCENTER            11
#define TTFUN_HCENTER            12
#define TTFUN_RELATIVE1V         13
#define TTFUN_RELATIVE2V         14
#define TTFUN_RELATIVE1H         15
#define TTFUN_RELATIVE2H         16
#define TTFUN_SIDE1              17    
#define TTFUN_SIDE2              18
#define TTFUN_FLEX               19
#define TTFUN_SCALE3             20
#define TTFUN_SHIFT1             21
#define TTFUN_SHIFT2             22
#define TTFUN_IP1                23  
#define TTFUN_IP2                24
#define TTFUN_IPN                25
#define TTFUN_SHP1               26  
#define TTFUN_SHP2               27
#define TTFUN_SHPN               28
#define TTFUN_RANGE              29
#define TTFUN_OBLIQUE            30
#define TTFUN_NUM                31    /* 1..30 */


#define FDEF(name)            op_pushb1, name, op_fdef,
#define ENDF                  op_endf,
#define CALL(name)            op_pushb1, name, op_call
#define WCVT(name)            op_pushb1, name, op_swap, op_wcvtf
#define PUSH1(v)              op_pushb1, (v)
#define PUSH2(v1, v2)         op_pushb1+1, (v1), (v2)
#define PUSH3(v1, v2, v3)     op_pushb1+2, (v1), (v2), (v3)
#define PUSH4(v1, v2, v3, v4) op_pushb1+3, (v1), (v2), (v3), (v4)
#define PUSH5(v1,v2,v3,v4,v5) op_pushb1+4, (v1), (v2), (v3), (v4), (v5)


static const UBYTE FontProg[] = {


   
/******* SET ZONE FUNCTION
 *
 * Args: flat_pos
 *
 */
FDEF(TTFUN_SET_ZONE)
   PUSH1(TMPPNT),
   op_swap,
   op_miap,
   PUSH1(TMPPNT),
   op_mdap | SUBOP_R,
ENDF




/******* COPY ZONE FUNCTION
 *
 * Args: from_cvt, to_cvt
 *
 */
FDEF(TTFUN_COPY_ZONE)
   op_rcvt,
   op_round,
   op_wcvtp,
ENDF





/******* STEM SNAP WIDTH FUNCTION
 *
 * Args: std_ci, std_cvt, snap_ci, snap_cvt, width, storage
 *
 */
FDEF(TTFUN_STEM_SNAP_WIDTH)
   op_mppem,
   op_gteq,
   op_if,

      /* Use std */
      op_rcvt,
      op_round,
      PUSH1(ONEPIXEL/2),
      op_max,
      op_swap, op_pop, op_swap, op_pop, op_swap, op_pop,
      CALL(TTFUN_WRITE_STEM),
      
   op_else,
      op_pop,
      op_mppem,
      op_gteq,
      op_if,
   
         /* Use snap */
         op_rcvt,
         op_round,
         PUSH1(ONEPIXEL/2),
         op_max,
    op_swap,
         op_pop, 
         CALL(TTFUN_WRITE_STEM),
         
      /* Use real width. */
      op_else,
         op_pop,
         WCVT(TMPCVT),
         PUSH1(TMPCVT),
         op_rcvt,
         op_round,
         PUSH1(ONEPIXEL/2),
         op_max,
         CALL(TTFUN_WRITE_STEM),
      op_eif,
   
   op_eif,
ENDF



   

/******* STEM STD WIDTH FUNCTION
 *
 * Args: std_ci, std_cvt, width, storage
 *
 */
FDEF(TTFUN_STEM_STD_WIDTH)
   op_mppem,
   op_gteq,
   op_if,
   
      /* Use std */
      op_rcvt,
      op_round,
      PUSH1(ONEPIXEL/2),
      op_max,
      op_swap,
      op_pop,
      CALL(TTFUN_WRITE_STEM),
      
   /* Use real width. */
   op_else,
      op_pop,
      WCVT(TMPCVT),
      PUSH1(TMPCVT),
      op_rcvt,
      op_round,
      PUSH1(ONEPIXEL/2),
      op_max,
      CALL(TTFUN_WRITE_STEM),
   op_eif,
   
ENDF





/******* SHIFT BLUE ZONE FUNCTION
 *
 * Args: cvt
 *
 */
FDEF(TTFUN_SHIFT_BLUE_ZONE)
   PUSH5(TMPPNT1, TMPPNT1, TMPPNT, TMPPNT1, 5),
   op_cindex,
   op_miap,
   op_srp0,
   op_mdrp | SUBOP_mMRGR,
   op_gc,
   op_wcvtp,
ENDF





/******* ALIGN BLUE ZONE FUNCTION
 *
 * Args: cvt
 *
 */
FDEF(TTFUN_ALIGN_BLUE_ZONE)
   PUSH5(TMPPNT1, TMPPNT1, TMPPNT, TMPPNT1, 5),
   op_cindex,
   op_miap,
   op_srp0,
   op_mdrp | SUBOP_ROUND,
   op_gc,
   op_wcvtp,
ENDF





/******* COPY FAMILY FUNCTION
 *
 * Args: base_cvt
 *
 */
FDEF(TTFUN_COPY_FAMILY)
   op_dup,
   PUSH1(1),
   op_add,
   op_rcvt,
   op_wcvtp,
ENDF





/******* WRITE STEM FUNCTION
 *
 * Args: width, storage
 *
 */
FDEF(TTFUN_WRITE_STEM)
   op_dup,    /* -| width, width, storage */
   op_dup,    /* -| width, width, width, storage */
   op_add,    /* -| 2*width, width, storage, */
   op_odd,    /* -| odd/even, width, storage */
   PUSH2(1, 4),     /* -| 4, 1, odd/even, width, storage */
   op_cindex,     /* -| storage, 1, odd/even, width, storage */
   op_add,
   op_swap,   /* -| odd/even, storage+1, width, storage */
   op_ws,
   op_ws,
ENDF





/******* VERTICAL FUNCTION
 *
 * Args: -*none*-
 *
 */
FDEF(TTFUN_VERTICAL)
   op_svcta | SUBOP_X,
   PUSH1(TWILIGHT),
   op_szps,
ENDF





/******* HORIZONTAL FUNCTION
 *
 * Args: -*none*-
 *
 */
FDEF(TTFUN_HORIZONTAL)
   PUSH1(TWILIGHT),
   op_svcta,
   op_szps,
ENDF





/******* CENTER VSTEM FUNCTION
 *
 * Args: p1, p2, p3, p4, c, tz1, width
 *
 */
FDEF(TTFUN_VCENTER)

   /* Set rounding state for the center. */
   PUSH2(1, 8),
   op_cindex,
   op_add,
   op_rs,
   op_if,
       op_rthg,
   op_else,
       op_rtg,
   op_eif,

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 6),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 6),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 2, 5),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 3, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,


   /* Move/round center. */
   WCVT(TMPCVT),     /* c */
   PUSH2(TMPPNT, TMPCVT),
   op_miap| SUBOP_R,
   op_rtg,

   /* Align all points to the center. */
   op_dup, op_dup, PUSH1(1), op_add,
   op_alignrp, op_alignrp,   /* tz1, tz1+1 */


   /* Compute the width. */
   op_swap,
   op_rs,
   PUSH1(CURVEPHASE),
   op_sub,
   op_swap,


   /* -| tz1, width */
   op_dup,
   op_dup,
   op_dup,
   op_srp0,
   PUSH1(4), op_cindex,
   op_neg,     /* -| (-width/2), tz1, tz1, tz1, width */
   op_shpix,      
   PUSH1(2),
   op_add,
   op_alignrp,    /* -| tz1+2, tz1, width */


   /* Do the other side. */
   /* -| tz1, width */
   PUSH1(1),
   op_add,
   op_dup,
   op_dup,     /* -| tz1+1, tz1+1, tz1+1, width */
   op_srp0,
   op_roll,    /* -| width, tz1+1, tz1+1 */
   op_shpix,      
   PUSH1(2),
   op_add,
   op_alignrp,    /* -| tz1+3 */

   /* Done. */
ENDF






/******* CENTER HSTEM FUNCTION
 *
 * Args: p1, p2, c, tz1, width
 *
 */
FDEF(TTFUN_HCENTER)

   /* Set,rounding state for the center. */
   PUSH2(1, 6),
   op_cindex,
   op_add,
   op_rs,
   op_if,
       op_rthg,
   op_else,
       op_rtg,
   op_eif,

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 4),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,


   /* Move/round center. */
   WCVT(TMPCVT),  /* c */
   PUSH2(TMPPNT, TMPCVT),
   op_miap| SUBOP_R,
   op_rtg,

   /* Align all points to the center. */
   op_dup, op_dup, PUSH1(1), op_add,
   op_alignrp, op_alignrp,   /* tz1, tz1+1 */


   /* Compute the width. */
   op_swap,
   op_rs,
   PUSH1(CURVEPHASE),
   op_sub,
   op_swap,


   /* -| tz1, width */
   op_dup,
   PUSH1(3), op_cindex,
   op_neg,     /* -| -width, tz1, tz1, width */
   op_shpix,      

   /* Do the other side. */
   /* -| tz1, width */
   PUSH1(1),
   op_add,
   op_swap,    /* -| width, tz1+1 */
   op_shpix,      

   /* Done. */
ENDF





/******* RELATIVE1V STEM FUNCTION
 *
 * Args: p1, p2, p3, p4, ref, tz1, width
 *
 */
FDEF(TTFUN_RELATIVE1V)

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 6),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 6),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 2, 5),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 3, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   /* Move/round side. */
   op_srp0,
   op_dup,
   op_mdrp | SUBOP_MmRGR,

   /* Align points on the left side. */
   op_dup, PUSH1(1), op_add, op_dup, op_dup, op_dup,
   PUSH1(1), op_add,  /* -| tz1+2, tz1+1, tz1+1, tz+1, tz, width */
   op_alignrp,
   op_alignrp,

   /* Align right side */
   op_srp0,    /* -| tz1+1, tz1, width */
   op_roll,
   op_rs,
   op_dup,
   op_add,     /* -| width*2, tz1+1, tz1 */
   op_shpix,
   PUSH1(3),
   op_add,
   op_alignrp,    /* -| tz1+3 */

ENDF





/******* RELATIVE2V STEM FUNCTION
 *
 * Args: p1, p2, p3, p4, ref, tz1, width
 *
 */
FDEF(TTFUN_RELATIVE2V)

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 6),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 6),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 2, 5),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 3, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,


   /* Move/round side. */
   op_srp0,
   op_dup,
   PUSH1(1), op_add,
   op_mdrp | SUBOP_MmRGR,

   /* Align points on the left side. */
   op_dup, op_dup, op_dup, op_dup,
   PUSH1(3), op_add,  /* -| tz1+3, tz1, tz1, tz1, tz1, width */
   op_alignrp,
   op_alignrp,


   /* Align left side */
   op_srp0,    /* -| tz1, tz1, width */
   op_roll,
   op_rs,
   op_dup,
   op_add,
   op_neg,
   op_shpix,      /* -| -2*width, tz1, tz1 */
   PUSH1(2), op_add,
   op_alignrp,    /* -| tz1+2 */

ENDF





/******* RELATIVE1H STEM FUNCTION
 *
 * Args: p1, p2, ref, tz1, width
 *
 */
FDEF(TTFUN_RELATIVE1H)

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 4),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   /* Move/round side. */
   op_srp0,
   op_dup,
   op_mdrp | SUBOP_MmRGR,


   /* Align all point to the lower side. */
   PUSH1(1), op_add, op_dup,
   op_alignrp,

   /* Align right side */
   op_swap,
   op_rs,
   op_dup,
   op_add,
   op_shpix,

ENDF





/******* RELATIVE2H STEM FUNCTION
 *
 * Args: p1, p2, ref, tz1, width
 *
 */
FDEF(TTFUN_RELATIVE2H)

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 4),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,


   /* Move/round side. */
   op_srp0,
   op_dup,
   PUSH1(1), op_add,
   op_mdrp | SUBOP_MmRGR,

   /* Align all points to the center. */
   op_dup, op_alignrp,

   /* Align left side */
   op_swap,
   op_rs,
   op_dup,
   op_add,
   op_neg,
   op_shpix,

ENDF





/******* SIDE1 STEM FUNCTION
 *
 * Args: p1, p2, zone, tz1, width
 *
 */
FDEF(TTFUN_SIDE1)

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 4),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,

   /* Move/round side. */
   PUSH2(TMPPNT, TMPPNT),
   op_srp0, op_swap, op_miap | SUBOP_R,

   /* Align all points to the side. */
   op_dup, PUSH1(1), op_add, op_dup, op_roll,
   op_alignrp, op_alignrp,

   /* Align first side */
   op_swap,
   op_rs,
   op_dup,
   op_add,
   PUSH1(CURVEPHASE),
   op_sub,
   op_shpix,

ENDF





/******* SIDE2 STEM FUNCTION
 *
 * Args:  p1, p2, zone, tz1, width
 *
 */
FDEF(TTFUN_SIDE2)

   /* Create the stem in the twilight zone. */
   WCVT(TMPCVT),
   PUSH2(TMPCVT, 4),
   op_cindex,
   op_swap,
   op_miap,

   WCVT(TMPCVT),
   PUSH3(TMPCVT, 1, 4),
   op_cindex,
   op_add,
   op_swap,
   op_miap,


   /* Move/round side. */
   PUSH2(TMPPNT, TMPPNT),
   op_srp0, op_swap, op_miap | SUBOP_R,

   /* Align all points to the side. */
   op_dup, op_dup, PUSH1(1), op_add, 
   op_alignrp, op_alignrp,

   /* Align second side */
   op_swap,
   op_rs,
   op_dup,
   op_add,
   PUSH1(CURVEPHASE),
   op_sub,
   op_neg,
   op_shpix,

ENDF


    


/******* FLEX FUNCTION
 *
 * Args on the stack: pnt_start, pnt_mid, ref_pos, pnt_mid,
 *       pnt_start, pnt_mid, cnt, p1, p2, ....
 *
 */
FDEF(TTFUN_FLEX)
   op_srp0,
   op_alignrp,
   op_wcvtf,
   op_rcvt,
   op_shpix,
   op_srp1,
   op_srp2,
   op_sloop,
   op_ip,
ENDF





/******* SCALE3 FUNCTION
 *
 * Args: cnt, p1, p2, ... 
 *
 */
FDEF(TTFUN_SCALE3)
   PUSH4(GLYPHZONE, TMPPNT1, TMPPNT, TMPPNT1),
   op_pushw1, HIBYTE(-31), LOBYTE(-31),
   PUSH3(TMPPNT, 0, TMPPNT1),
   op_pushw1, HIBYTE(1000), LOBYTE(1000),
   op_scfs,
   op_scfs,
   op_shpix,
   op_srp1,
   op_srp2,
   op_szp2,
   op_sloop,
   op_ip,
ENDF





/******* SHIFT1 FUNCTION
 *
 * Args: cnt reduction p1 p2 ...
 *
 */
FDEF(TTFUN_SHIFT1)
   op_sloop,
   op_rs,
   op_neg,
   op_shpix,
ENDF




   
/******* SHIFT2 FUNCTION
 *
 * Args: cnt reduction p1 p2 ...
 *
 */
FDEF(TTFUN_SHIFT2)
   op_sloop,
   op_rs,
   op_shpix,
ENDF





/******* IP1 FUNCTION
 *
 * Args: rp1, rp2, p1
 *
 */
FDEF(TTFUN_IP1)
   op_srp1,
   op_srp2,
   op_ip,
ENDF





/******* IP2 FUNCTION
 *
 * Args: rp1, rp2, p1, p2
 *
 */
FDEF(TTFUN_IP2)
   op_srp1,
   op_srp2,
   op_ip,
   op_ip,
ENDF





/******* IPN FUNCTION
 *
 * Args: rp1, rp2, cnt, p1, p2
 *
 */
FDEF(TTFUN_IPN)
   op_srp1,
   op_srp2,
   op_sloop,
   op_ip,
ENDF





/******* SHP1 FUNCTION
 *
 * Args: rp, p
 *
 */
FDEF(TTFUN_SHP1)
   op_srp1,
   op_shp,
ENDF





/******* SHP2 FUNCTION
 *
 * Args: rp, p1, p2
 *
 */
FDEF(TTFUN_SHP2)
   op_srp1,
   op_shp,
   op_shp,
ENDF


    

    
/******* SHPN FUNCTION
 *
 * Args: rp, cnt, p1, p2
 *
 */
FDEF(TTFUN_SHPN)
   op_srp1,
   op_sloop,
   op_shp,
ENDF





/******* RANGE FUNCTION
 *
 * Args: p
 *
 */
FDEF(TTFUN_RANGE)
   op_dup,
   PUSH1(1),
   op_add,
ENDF




/******* RANGE FUNCTION
 *
 * Args: pos_x, pos_y,
 *
 */
FDEF(TTFUN_OBLIQUE)
   op_svcta | SUBOP_Y,
   PUSH1(TMPPNT1),
   op_swap,
   op_scfs,
   PUSH2(TMPPNT, 0),
   op_scfs,
   op_svcta | SUBOP_X,
   PUSH1(TMPPNT1),
   op_swap,
   op_scfs,
   PUSH2(TMPPNT, 0),
   op_scfs,
   PUSH2(TMPPNT, TMPPNT1),
   op_spvtl,
ENDF

};


/***** LOCAL TYPES */


/***** STATIC FUNCTIONS */

/***
** Function: GetVStemWidth
**
** Description:
**   This function allocates a storage entry for the 
**   width of a vertical stem;
***/
static short GetVStemWidth(WeightControl *weight, const funit width)
{
   StemWidth *newwidths = NULL;
   short entry = 0;
   USHORT j;

   if (weight->cnt_vw >= weight->max_vw) {
      newwidths = Realloc(weight->vwidths,
                          sizeof(StemWidth)*(weight->max_vw+BUFSIZE));
      if (newwidths == NULL) {
         entry = NOMEM;
      } else {
         weight->vwidths = newwidths;
         weight->max_vw += BUFSIZE;
      }
   }

   if (entry != NOMEM) {
      for (j=0; j<weight->cnt_vw; j++) {
         if (weight->vwidths[j].width==width) {
            entry = (short)weight->vwidths[j].storage;
            break;
         }
      }

      if (j==weight->cnt_vw) {
         weight->vwidths[weight->cnt_vw].storage = weight->storage;
         weight->vwidths[weight->cnt_vw].width = width;
         entry = (short)weight->storage;
         weight->storage += 2;
         weight->cnt_vw++;
      }
   }

   return entry;
}


/***
** Function: GetHStemWidth
**
** Description:
**   This function allocates a storage entry for the 
**   width of a vertical stem;
***/
static short GetHStemWidth(WeightControl *weight, const funit width)
{
   StemWidth *newwidths = NULL;
   short entry = 0;
   USHORT j;

   if (weight->cnt_hw >= weight->max_hw) {
      newwidths = Realloc(weight->hwidths,
                          sizeof(StemWidth)*(weight->max_hw+BUFSIZE));
      if (newwidths == NULL) {
         entry = NOMEM;
      } else {
         weight->hwidths = newwidths;
         weight->max_hw += BUFSIZE;
      }
   }

   if (entry != NOMEM) {
      for (j=0; j<weight->cnt_hw; j++) {
         if (weight->hwidths[j].width==width) {
            entry = (short)weight->hwidths[j].storage;
            break;
         }
      }

      if (j==weight->cnt_hw) {
         weight->hwidths[weight->cnt_hw].storage = weight->storage;
         weight->hwidths[weight->cnt_hw].width = width;
         entry = (short)weight->storage;
         weight->storage += 2;
         weight->cnt_hw++;
      }
   }

   return entry;
}




/***** GLOBAL FUNCTIONS */

/***
** Function: SetZone
**
** Description:
**   This function initiate an alignment zone
**   by creating an appropriate point in the
**   twilight zone.
***/
USHORT SetZone(UBYTE *prep, USHORT tp, const short cvt)
{
   /* Set up the zone. */
   if (cvt>255) {
      prep[tp++] = op_pushw1;
      prep[tp++] = HIBYTE(cvt);
      prep[tp++] = LOBYTE(cvt);
      prep[tp++] = op_pushb1;
      prep[tp++] = TTFUN_SET_ZONE;
   } else {
      prep[tp++] = op_pushb1 + 1;
      prep[tp++] = (UBYTE)cvt;
      prep[tp++] = TTFUN_SET_ZONE;
   }
   prep[tp++] = op_call;

   return tp;
}



/***
** Function: CopyZone
**
** Description:
**   This function copies a cvt entry, representing an
**   alignment zone, to the cvt used for a particular hstem.
***/
USHORT CopyZone(UBYTE *prep, short tp, short *args, const short ta)
{
   args[0] = TTFUN_COPY_ZONE;
   args[1] = (short)((ta-2)/2);
   AssembleArgs(args, ta, prep, &tp);
   prep[tp++] = op_loopcall;

   return (USHORT)tp;
}



/***
** Function: CopyFamilyBlue
**
** Description:
**   This function copies a cvt entry, representing a
**   family blue zone, to the cvt used for a particular hstem.
***/
USHORT CopyFamilyBlue(UBYTE *prep, short tp, short *args, const short ta)
{
   args[0] = TTFUN_COPY_FAMILY;
   args[1] = (short)(ta-2);
   AssembleArgs(args, ta, prep, &tp);
   prep[tp++] = op_loopcall;

   return (USHORT)tp;
}



/***
** Function: AlignFlat
**
** Description:
**   This function creates a cvt entry for
**   a particular hstem.
***/
USHORT AlignFlat(UBYTE *prep, short tp, short *args, const short ta)
{
   args[0] = TTFUN_ALIGN_BLUE_ZONE;
   args[1] = (short)(ta-2);
   AssembleArgs(args, ta, prep, &tp);
   prep[tp++] = op_loopcall;

   return (USHORT)tp;
}



/***
** Function: AlignOvershoot
**
** Description:
**   This function creates a cvt entry for
**   a particular hstem.
***/
USHORT AlignOvershoot(UBYTE *prep, short tp, short *args, const short ta)
{
   args[0] = TTFUN_SHIFT_BLUE_ZONE;
   args[1] = (short)(ta-2);
   AssembleArgs(args, ta, prep, &tp);
   prep[tp++] = op_loopcall;

   return (USHORT)tp;
}


/***
** Function: GetTopPos
**
** Description:
**   This function allocates a cvt entry for the 
**   top side of a horizontal stem;
***/
short GetTopPos(const Blues *blues,
                AlignmentControl *align,
                const funit pos)
{
   short entry = UNDEF;
   const funit *bluevals;
   short fuzz;
   USHORT i, j;

   bluevals = &(blues->bluevalues[0]);
   fuzz = blues->blueFuzz;

   /* Check if it is within a zone. */
   for (i=0; i<blues->blue_cnt; i+=2) {
      if (((bluevals[i]-fuzz)<=pos) && ((bluevals[i+1]+fuzz)>=pos))
         break;
   }

   /* Record the position? */
   if (i!=blues->blue_cnt) {
      i /= 2;

      /* Is the position already mapped to a cvt entry? */
      for (j=0; j<align->top[i].cnt; j++) {
         if (align->top[i].pos[j].y==pos) {
            entry = (short)align->top[i].pos[j].cvt;
            break;
         }
      }

      if (j==align->top[i].cnt) {

         /* Allocate the BlueZone cvt's */
         if (align->top[i].cnt==0) {
            align->top[i].blue_cvt = align->cvt;
            align->cvt +=2;
         }

         align->top[i].pos[align->top[i].cnt].cvt = align->cvt;
         align->top[i].pos[align->top[i].cnt].y = pos;
         entry = (short)align->cvt;
         align->cvt+=2;
         align->top[i].cnt++;
      }
   }

   return entry;
}


/***
** Function: GetBottomPos
**
** Description:
**   This function allocates a cvt entry for the 
**   top side of a horizontal stem;
***/
short GetBottomPos(const Blues *blues,
                   AlignmentControl *align,
                   const funit pos)
{
   short entry = UNDEF;
   const funit *bluevals;
   short fuzz;
   USHORT i, j;

   bluevals = &(blues->otherblues[0]);
   fuzz = blues->blueFuzz;

   /* Check if it is within a zone. */
   for (i=0; i<blues->oblue_cnt; i+=2) {
      if (((bluevals[i]-fuzz)<=pos) && ((bluevals[i+1]+fuzz)>=pos))
         break;
   }


   /* Record the position? */
   if (i!=blues->oblue_cnt) {
      i /= 2;

      /* Is the position already mapped to a cvt entry? */
      for (j=0; j<align->bottom[i].cnt; j++) {
         if (align->bottom[i].pos[j].y==pos) {
            entry = (short)align->bottom[i].pos[j].cvt;
            break;
         }
      }

      if (j==align->bottom[i].cnt) {

         /* Allocate the BlueZone and FamilyBlue cvt's */
         if (align->bottom[i].cnt==0) {
            align->bottom[i].blue_cvt = align->cvt++;
         }

         align->bottom[i].pos[align->bottom[i].cnt].cvt = align->cvt;
         align->bottom[i].pos[align->bottom[i].cnt].y = pos;
         entry = (short)align->cvt;
         align->cvt+=2;
         align->bottom[i].cnt++;
      }
   }

   return entry;
}


/***
** Function: CutInSize
**
** Description:
**   This function computes the cut in size
**   of a stem, given a master width and the
**   width of the stem. This is done with the
**   StdVW==2.0 pixel treshold and the thinn
**   and wide cut in values.
***/
USHORT CutInSize(const funit width,
                 const funit master,
                 const USHORT tresh,
                 const funit upem)
{
   USHORT cis, ci1, ci2;

   /*lint -e776 */
   if (width > master) {
      ci1 = (USHORT)((long)upem * SMALL_UPPER / ONEPIXEL /
                     (long)(width - master));
      ci2 = (USHORT)((long)upem * LARGE_UPPER / ONEPIXEL /
                     (long)(width - master));
   } else if (width < master) {
      ci1 = (USHORT)((long)upem * SMALL_LOWER / ONEPIXEL /
                     (long)(master - width));
      ci2 = (USHORT)((long)upem * LARGE_LOWER / ONEPIXEL /
                     (long)(master - width));
   } else {
      ci1 = INFINITY;  
      ci2 = INFINITY;
   }
   /*lint +e776 */

   if (ci1 < tresh) {
      cis = ci1;
   } else if (ci2 < tresh) {
      cis = tresh;
   } else {
      cis = ci2;
   }

   return cis;
}


/***
** Function: SnapStemArgs
**
** Description:
**   
***/
USHORT SnapStemArgs(short *args, USHORT ta,
                    const funit width,
                    const USHORT std_cvt,
                    const USHORT snap_cvt,
                    const USHORT std_ci,
                    const USHORT snap_ci,
                    const USHORT storage)
{
   args[ta++] = (short)std_ci;
   args[ta++] = (short)std_cvt;
   args[ta++] = (short)snap_ci;
   args[ta++] = (short)snap_cvt;
   args[ta++] = (short)(width/2);
   args[ta++] = (short)storage;

   return ta;
}



/***
** Function: StdStemArgs
**
** Description:
**   
***/
USHORT StdStemArgs(short *args, USHORT ta,
                   const funit width,
                   const USHORT std_cvt,
                   const USHORT std_ci,
                   const USHORT storage)
{
   args[ta++] = (short)std_ci;
   args[ta++] = (short)std_cvt;
   args[ta++] = (short)(width/2);
   args[ta++] = (short)storage;

   return ta;
}



/***
** Function: CreateStdStems
**
** Description:
**   
***/
USHORT CreateStdStems(UBYTE *prep, USHORT tp, const short cnt)
{
   if (cnt>255) {
      prep[tp++] = op_pushw1;
      prep[tp++] = HIBYTE(cnt);
      prep[tp++] = LOBYTE(cnt);
      prep[tp++] = op_pushb1;
      prep[tp++] = TTFUN_STEM_STD_WIDTH;
   } else {
      prep[tp++] = op_pushb1 + 1;
      prep[tp++] = (UBYTE)cnt;
      prep[tp++] = TTFUN_STEM_STD_WIDTH;
   }

   prep[tp++] = op_loopcall;

   return tp;
}



/***
** Function: CreateSnapStems
**
** Description:
**   
***/
USHORT CreateSnapStems(UBYTE *prep, USHORT tp, const short cnt)
{
   if (cnt>255) {
      prep[tp++] = op_pushw1;
      prep[tp++] = HIBYTE(cnt);
      prep[tp++] = LOBYTE(cnt);
      prep[tp++] = op_pushb1;
      prep[tp++] = TTFUN_STEM_SNAP_WIDTH;
   } else {
      prep[tp++] = op_pushb1 + 1;
      prep[tp++] = (UBYTE)cnt;
      prep[tp++] = TTFUN_STEM_SNAP_WIDTH;
   }

   prep[tp++] = op_loopcall;

   return tp;
}




/***
** Function: tt_GetFontProg
**
** Description:
**   This function returns the static font
**   font program.
***/
const UBYTE *tt_GetFontProg(void)
{
   return FontProg;
}




/***
** Function: tt_GetFontProgSize
**
** Description:
**   This function returns the size of the
**   static font program.
***/
USHORT tt_GetFontProgSize(void)
{
   return (USHORT)sizeof(FontProg);
}




/***
** Function: tt_GetNumFuns
**
** Description:
**   This function returns the number of functions
**   defined in the static font program.
***/
USHORT tt_GetNumFuns(void)
{
   return (USHORT)TTFUN_NUM;
}



/***
** Function: EmitFlex
**
** Description:
**   Convert a T1 flex hint into a TrueType IP[] 
**   intruction sequence that will reduce a flex
**   that is flatter than a given height.
***/
errcode EmitFlex(short *args,
                 short *pcd,
                 const funit height,
                 const short start,
                 const short mid,
                 const short last)
{
   errcode status = SUCCESS;
   int i;

   /* Enough space for the instructions? */
   args[(*pcd)++] = TTFUN_FLEX;
   args[(*pcd)++] = start;
   args[(*pcd)++] = mid;
   args[(*pcd)++] = (short)height;
   args[(*pcd)++] = TMPCVT;
   args[(*pcd)++] = TMPCVT;
   args[(*pcd)++] = mid;
   args[(*pcd)++] = start;
   args[(*pcd)++] = mid;

   /* Push the flex points onto the stack. */
   args[(*pcd)++] = (short)(last-start-2);
   for (i=start+(short)1; i<last; i++)
      if (i!=mid)
         args[(*pcd)++] = (short)i;

   return status;
}




/***
** Function: ReduceDiagonals
**
** Description:
**   This function generates the TT instructions
**   that will shrink the outline, in order to
**   control the width of diagonals. This implementation
**   can probably be improved.
***/
short ReduceDiagonals(const Outline *paths,
                      UBYTE *pgm, short *pc,
                      short *args,  short *pcd)
{
   short cw[MAXTHINPNTS];
   short ccw[MAXTHINPNTS];
   short targ[MAXTHINPNTS];
   const Outline *path;
   Point *pts;
   short i,j;
   short cwi = 0, ccwi = 0;
   short prev;
   short n,m;
   short prev_cw, prev_ccw;
   short ta;



   /* Collect points on left and right side that are diagonals. */
   i = 0;
   for (path = paths; path && ccwi<MAXTHINPNTS && cwi<MAXTHINPNTS;
   path=path->next) {

      pts = &path->pts[0];
      prev_cw = FALSE;
      prev_ccw = FALSE;

      /* Are the first and last point coinciding? */
      if (pts[path->count-1].x!=pts[0].x ||
          pts[path->count-1].y!=pts[0].y)
         prev = (short)(path->count-(short)1);
      else
         prev = (short)(path->count-(short)2);

      /* Special case the first point. */
      if (!OnCurve(path->onoff, prev) ||
          (pts[0].x != pts[prev].x &&
           ABS(pts[0].x - pts[prev].x) < ABS(pts[0].y - pts[prev].y)*8)) {
         if (pts[0].y>pts[prev].y+20) {
            if (pts[prev].y<=pts[prev-1].y)
               cw[cwi++] = (short)(i+(short)path->count-1);
            cw[cwi++] = i;
            prev_cw = TRUE;
            prev_ccw = FALSE;
         } else if (pts[0].y<pts[prev].y-20) {
            if (pts[prev].y>=pts[prev-1].y)
               ccw[ccwi++] = (short)(i+(short)path->count-1); 
            ccw[ccwi++] = i;
            prev_cw = FALSE;
            prev_ccw = TRUE;
         }
      }


      for (j=1; j<(short)path->count &&
             ccwi<MAXTHINPNTS && cwi<MAXTHINPNTS; j++) {
         i++;
         if (!OnCurve(path->onoff, j-1) ||
             (pts[j].x != pts[j-1].x &&
              ABS(pts[j].x - pts[j-1].x) < ABS(pts[j].y - pts[j-1].y)*8)) {
            if (pts[j].y>pts[j-1].y+20) {
               if (!prev_cw)
                  cw[cwi++] = (short)(i-1);
               cw[cwi++] = i;
               prev_cw = TRUE; 
               prev_ccw = FALSE;
            } else if (pts[j].y<pts[j-1].y-20) {
               if (!prev_ccw)
                  ccw[ccwi++] = (short)(i-1);
               ccw[ccwi++] = i;
               prev_cw = FALSE;
               prev_ccw = TRUE;
            } else {
               prev_cw = FALSE;
               prev_ccw = FALSE;
            }
         } else {
            prev_cw = FALSE;
            prev_ccw = FALSE;
         }
      }
      i++;
   }


   /* Did we get all points? */
   if (ccwi>=MAXTHINPNTS || cwi>=MAXTHINPNTS) {
      LogError(MSG_WARNING, MSG_DIAG, NULL);
   }


   /* Any points to shift? */
   if (cwi || ccwi) {
      args[(*pcd)++] = STORAGE_DIAG;
      pgm[(*pc)++] = op_rs;
      pgm[(*pc)++] = op_if;
      pgm[(*pc)++] = op_svcta + SUBOP_X;

      /* Switch over to GLYPHZONE */
      pgm[(*pc)++] = op_szp2;
      args[(*pcd)++] = 1;


      ta = 3;

      /* Disable "cw[m] may not have been initialized".*/ /*lint -e644 */
      for (n=0; n<cwi; n=m) {
         for (m=(short)(n+1); m<cwi && cw[m]==cw[m-1]+1; m++); /*lint +e644 */
         if (m-n<=4) {
            for (i=n; i<m; i++)
               targ[ta++] = cw[i];
         } else {
            targ[0] = TTFUN_RANGE;
            targ[1] = (short)(m-n-1);
            targ[2] = cw[n];
            AssembleArgs(targ, ta, pgm, pc);
            pgm[(*pc)++] = op_loopcall;
            ta = 3;
         }
      }
      targ[0] = TTFUN_SHIFT1;
      targ[1] = cwi;
      targ[2] = STORAGE_DIAG;
      AssembleArgs(targ, ta, pgm, pc);
      pgm[(*pc)++] = op_call;


      /************ Shift back the left side of the glyph. */

      ta = 3;

      /* Disable "ccw[m] may not have been initialized".*/ /*lint -e644 */
      for (n=0; n<ccwi; n=m) {
         for (m=(short)(n+1); m<ccwi && ccw[m]==ccw[m-1]+1; m++); /*lint +e644 */
         if (m-n<=4) {
            for (i=n; i<m; i++)
               targ[ta++] = ccw[i];
         } else {
            targ[0] = TTFUN_RANGE;
            targ[1] = (short)(m-n-1);
            targ[2] = ccw[n];
            AssembleArgs(targ, ta, pgm, pc);
            pgm[(*pc)++] = op_loopcall;
            ta = 3;
         }
      }
      targ[0] = TTFUN_SHIFT2;
      targ[1] = ccwi;
      targ[2] = STORAGE_DIAG;
      AssembleArgs(targ, ta, pgm, pc);
      pgm[(*pc)++] = op_call;


#ifdef SYMETRICAL_REDUCTION

      /* The amount that the outline is shrunk is computed once at
      each size, in the pre-program. The outline is shrunk
      symetrically by the amount: 1/16 + (12 Funits)*size/UPEm.

      This approach yields more symmetrical results than shrinking
      the outline horizontally alone (see separate papers on the topic). */


      /* Same thing for the height... */
      i = 0;
      cwi = 0;
      ccwi = 0;
      for (path = paths; path && ccwi<MAXTHINPNTS && cwi<MAXTHINPNTS;
      path=path->next) {

         pts = &path->pts[0];

         /* Are the first and last point coinciding? */
         if (pts[path->count-1].y!=pts[0].y ||
             pts[path->count-1].x!=pts[0].x)
            prev = path->count-1;
         else
            prev = path->count-2;

         if (!OnCurve(path->onoff, prev) ||
             (pts[0].y != pts[prev].y &&
              ABS(pts[0].y - pts[prev].y) < ABS(pts[0].x - pts[prev].x)*8)) {
            if (pts[0].x>pts[prev].x+20) {
               if (pts[prev].x<=pts[prev-1].x)
                  cw[cwi++] = i+path->count-1;
               cw[cwi++] = i;
            } else if (pts[0].x<pts[prev].x-20) {
               if (pts[prev].x>=pts[prev-1].x)
                  ccw[ccwi++] = i+path->count-1; 
               ccw[ccwi++] = i;
            }
         }


         for (j=1; j<path->count && ccwi<MAXTHINPNTS && cwi<MAXTHINPNTS; j++) {
            i++;
            if (!OnCurve(path->onoff, j-1) ||
                (pts[j].y != pts[j-1].y &&
                 ABS(pts[j].y - pts[j-1].y) < ABS(pts[j].x - pts[j-1].x)*8)) {
               if (pts[j].x>pts[j-1].x+20) {
                  if (!cwi || cw[cwi-1]!=i-1)
                     cw[cwi++] = i-1;
                  cw[cwi++] = i;
               } else if (pts[j].x<pts[j-1].x-20) {
                  if (!ccwi || ccw[ccwi-1]!=i-1)
                     ccw[ccwi++] = i-1;
                  ccw[ccwi++] = i;
               }
            }
         }
         i++;
      }


      if (ccwi>=MAXTHINPNTS || cwi>=MAXTHINPNTS) {
         LogError(MSG_WARNING, MSG_DIAG, NULL);
      }


      /* Any points to shift? */
      if (cwi || ccwi) {
         pgm[(*pc)++] = op_svcta + SUBOP_Y;


         for (n=0; n<cwi; n=m) {
            for (m=n+1; m<cwi && cw[m]==cw[m-1]+1; m++);
            pgm[(*pc)++] = op_pushb1 + 2;
            pgm[(*pc)++] = cw[n];
            pgm[(*pc)++] = (UBYTE)(m-n-1);
            pgm[(*pc)++] = TTFUN_RANGE;
            pgm[(*pc)++] = op_loopcall;
         }
         pgm[(*pc)++] = op_pushb1+2;
         pgm[(*pc)++] = STORAGE_DIAG;
         pgm[(*pc)++] = cwi;
         pgm[(*pc)++] = TTFUN_SHIFT2;
         pgm[(*pc)++] = op_call;



         /************ Shift back the left side of the glyph. */


         for (n=0; n<ccwi; n=m) {
            for (m=n+1; m<ccwi && ccw[m]==ccw[m-1]+1; m++);
            pgm[(*pc)++] = op_pushb1 + 2;
            pgm[(*pc)++] = (UBYTE)ccw[n];
            pgm[(*pc)++] = (UBYTE)(m-n-1);
            pgm[(*pc)++] = TTFUN_RANGE;
            pgm[(*pc)++] = op_loopcall;
         }
         pgm[(*pc)++] = op_pushb1+2;
         pgm[(*pc)++] = STORAGE_DIAG;
         pgm[(*pc)++] = (UBYTE)ccwi;
         pgm[(*pc)++] = TTFUN_SHIFT1;
         pgm[(*pc)++] = op_call;
      }
#endif

      pgm[(*pc)++] = op_eif;
   }

   /* Args + num of args + function number. */
   return (short)(MAX(cwi, ccwi)+2); 
}




/***
** Function: ScaleDown3
**
** Description:
**   This function generates the TT instructions
**   that will scale down points 3%.
***/
void ScaleDown3(const Extremas *extr, const short xcnt, 
                UBYTE *pgm, short *pc,
                short *args, short *pcd)
{
   short i,j,offset, opc, opcd;

   /* Remember the state of the stacks. */
   opc = (*pc);
   opcd = (*pcd);

   args[(*pcd)++] = TTFUN_SCALE3;

   offset = (*pcd)++;
   args[offset] = 0;
   for (i=0; i<xcnt; i++) {
      if ((extr[i].rp1==UNDEF || extr[i].rp2==UNDEF)) {
         for (j=0; j<extr[i].n; j++) {
            args[(*pcd)++] = extr[i].pts[j];
         }
         args[offset] = (short)(args[offset] + extr[i].n);
      }
   }
   if (args[offset]>0) {
      pgm[(*pc)++] = op_call;
   } else {
      /* Back track. */
      (*pc) = opc;
      (*pcd) = opcd;
   }
}


/***
** Function: EmitIP
**
** Description:
**   This function generates the TT instructions
**   that will interpolate points that are either
**   within or between stem sides.
***/
void EmitIP(const Extremas *extr, const short xcnt, 
            UBYTE *pgm, short *pc,
            short *args, short *pcd,
            const short scale3offset)
{
   short i,j,num;
   short ones[MAXIP], twoes[MAXIP], nths[MAXIP];
   short cnt1, cnt2, cntn;


   /*lint -e530 -e644 */
   /* Shift extrems. */
   cnt1 = 0; cnt2 = 0; cntn = 0; num = 0;
   for (i=0; i<xcnt; i++) {
      short rp;

      /* Skip interpolations. */
      if (extr[i].rp1!=UNDEF && extr[i].rp2!=UNDEF)
         continue;

      /* Set the reference points. */
      if (extr[i].rp1!=UNDEF) {
         rp = (short)(extr[i].rp1+scale3offset);
      }  else {
         rp = (short)(extr[i].rp2+scale3offset);
      }

      if (extr[i].n==1) {
         if ((cnt1+2)>=MAXIP) {
            pgm[(*pc)++] = op_loopcall;
            args[(*pcd)++] = TTFUN_SHP1;
            args[(*pcd)++] = (short)(cnt1/2);
            for (j=0; j<cnt1; j++)
               args[(*pcd)++] = (short)ones[j];
            cnt1 = 0;
         }
         ones[cnt1++] = rp;
         ones[cnt1++] = extr[i].pts[0];
      } else if (extr[i].n==2) {
         if ((cnt2+3)>=MAXIP) {
            pgm[(*pc)++] = op_loopcall;
            args[(*pcd)++] = TTFUN_SHP2;
            args[(*pcd)++] = (short)(cnt2/3);
            for (j=0; j<cnt2; j++)
               args[(*pcd)++] = (short)twoes[j];
            cnt2 = 0;
         }
         twoes[cnt2++] = rp;
         twoes[cnt2++] = extr[i].pts[0];
         twoes[cnt2++] = extr[i].pts[1];
      } else {
         if ((cntn+2+extr[i].n)>=MAXIP) {
            pgm[(*pc)++] = op_loopcall;
            args[(*pcd)++] = TTFUN_SHPN;
            args[(*pcd)++] = num;
            for (j=0; j<cntn; j++)
               args[(*pcd)++] = (short)nths[j];
            cntn = 0;
            num = 0;
         }
         nths[cntn++] = rp;
         nths[cntn++] = extr[i].n;
         for (j=0; j<extr[i].n; j++) {
            nths[cntn++] = extr[i].pts[j];
         }
         num++;
      }
   }

   if (cnt1) {
      if (cnt1>2) {
         pgm[(*pc)++] = op_loopcall;
         args[(*pcd)++] = TTFUN_SHP1;
         args[(*pcd)++] = (short)(cnt1/2);
      } else {
         pgm[(*pc)++] = op_call;
         args[(*pcd)++] = TTFUN_SHP1;
      }
      for (i=0; i<cnt1; i++)
         args[(*pcd)++] = ones[i];
   }
   if (cnt2) {
      if (cnt2>3) {
         pgm[(*pc)++] = op_loopcall;
         args[(*pcd)++] = TTFUN_SHP2;
         args[(*pcd)++] = (short)(cnt2/3);
      } else {
         pgm[(*pc)++] = op_call;
         args[(*pcd)++] = TTFUN_SHP2;
      }
      for (i=0; i<cnt2; i++)
         args[(*pcd)++] = twoes[i];
   }
   if (cntn) {
      if (num>1) {
         pgm[(*pc)++] = op_loopcall;
         args[(*pcd)++] = TTFUN_SHPN;
         args[(*pcd)++] = num;
      } else {
         pgm[(*pc)++] = op_call;
         args[(*pcd)++] = TTFUN_SHPN;
      }
      for (i=0; i<cntn; i++)
         args[(*pcd)++] = (short)nths[i];
   }


   /* Interpolate the extrems. */
   cnt1 = 0; cnt2 = 0; cntn = 0; num = 0;
   for (i=0; i<xcnt; i++) {

      /* Skip interpolations. */
      if (extr[i].rp1==UNDEF || extr[i].rp2==UNDEF)
         continue;

      if (extr[i].n==1) {
         if ((cnt1+3)>=MAXIP) {
            pgm[(*pc)++] = op_loopcall;
            args[(*pcd)++] = TTFUN_IP1;
            args[(*pcd)++] = (short)(cnt1/2);
            for (j=0; j<cnt1; j++)
               args[(*pcd)++] = (short)ones[j];
            cnt1 = 0;
         }
         ones[cnt1++] = extr[i].rp1;
         ones[cnt1++] = extr[i].rp2;
         ones[cnt1++] = extr[i].pts[0];
      } else if (extr[i].n==2) {
         if ((cnt2+4)>=MAXIP) {
            pgm[(*pc)++] = op_loopcall;
            args[(*pcd)++] = TTFUN_IP2;
            args[(*pcd)++] = (short)(cnt2/3);
            for (j=0; j<cnt2; j++)
               args[(*pcd)++] = (short)twoes[j];
            cnt2 = 0;
         }
         twoes[cnt2++] = extr[i].rp1;
         twoes[cnt2++] = extr[i].rp2;
         twoes[cnt2++] = extr[i].pts[0];
         twoes[cnt2++] = extr[i].pts[1];
      } else {
         if ((cntn+3+extr[i].n)>=MAXIP) {
            pgm[(*pc)++] = op_loopcall;
            args[(*pcd)++] = TTFUN_IPN;
            args[(*pcd)++] = num;
            for (j=0; j<cntn; j++)
               args[(*pcd)++] = (short)nths[j];
            cntn = 0;
            num = 0;
         }
         nths[cntn++] = extr[i].rp1;
         nths[cntn++] = extr[i].rp2;
         nths[cntn++] = extr[i].n;
         for (j=0; j<extr[i].n; j++) {
            nths[cntn++] = extr[i].pts[j];
         }
         num++;
      }
   }

   if (cnt1) {
      if (cnt1>3) {
         pgm[(*pc)++] = op_loopcall;
         args[(*pcd)++] = TTFUN_IP1;
         args[(*pcd)++] = (short)(cnt1/3);
      } else {
         pgm[(*pc)++] = op_call;
         args[(*pcd)++] = TTFUN_IP1;
      }
      for (i=0; i<cnt1; i++)
         args[(*pcd)++] = (short)ones[i];
   }
   if (cnt2) {
      if (cnt2>4) {
         pgm[(*pc)++] = op_loopcall;
         args[(*pcd)++] = TTFUN_IP2;
         args[(*pcd)++] = (short)(cnt2/4);
      } else {
         pgm[(*pc)++] = op_call;
         args[(*pcd)++] = TTFUN_IP2;
      }
      for (i=0; i<cnt2; i++)
         args[(*pcd)++] = (short)twoes[i];
   }
   if (cntn) {
      if (num>1) {
         pgm[(*pc)++] = op_loopcall;
         args[(*pcd)++] = TTFUN_IPN;
         args[(*pcd)++] = num;
      } else {
         pgm[(*pc)++] = op_call;
         args[(*pcd)++] = TTFUN_IPN;
      }
      for (i=0; i<cntn; i++)
         args[(*pcd)++] = (short)nths[i];
   }
   /*lint +e530 +e644 */
}




/***
** Function: EmitVerticalStem
**
** Description:
**   This function generates the code that
**   will initiate the graphics state of the
**   TrueType interpreter for the grid fitting
**   of vertical stems.
***/
void EmitVerticalStems(UBYTE *pgm, short *pc, short *args, short *pcd)
{
   pgm[(*pc)++] = op_call;
   args[(*pcd)++] = TTFUN_VERTICAL;
}





/***
** Function: EmitHorizontalStem
**
** Description:
**   This function generates the code that
**   will initiate the graphics state of the
**   TrueType interpreter for the grid fitting
**   of vertical stems.
***/
void EmitHorizontalStems(UBYTE *pgm, short *pc, short *args, short *pcd)
{
   pgm[(*pc)++] = op_call;
   args[(*pcd)++] = TTFUN_HORIZONTAL;
}





/***
** Function: EmitVStem
**
** Description:
**   This function generates the code that
**   will create and grid fit points in the
**   twilight zone, corresponding to a vstem.
***/
errcode EmitVStem(UBYTE *pgm, short *pc,
                  short *args, short *pcd,
                  struct T1Metrics *t1m,
                  const funit width,
                  const funit real_side1,
                  const funit real_side2,
                  const funit side1,
                  const funit side2,
                  const short rp,
                  const enum aligntype align,
                  const short ref)
{
                     errcode status = SUCCESS;
                     short w_storage;

                     if ((w_storage = GetVStemWidth(GetWeight(t1m), width))==NOMEM) {
                        SetError(status = NOMEM);
                     } else {

                        pgm[(*pc)++] = op_call;
                        switch (align) {
                           case at_centered:
                              args[(*pcd)++] = TTFUN_VCENTER;
                              args[(*pcd)++] = (short)real_side1;
                              args[(*pcd)++] = (short)real_side2;
                              args[(*pcd)++] = (short)side1;
                              args[(*pcd)++] = (short)side2;
                              args[(*pcd)++] = (short)((side1+side2)/2);
                              args[(*pcd)++] = rp;
                              args[(*pcd)++] = w_storage;
                              break;

                           case at_relative1:
                              args[(*pcd)++] = TTFUN_RELATIVE1V;
                              args[(*pcd)++] = (short)real_side1;
                              args[(*pcd)++] = (short)real_side2;
                              args[(*pcd)++] = (short)side1;
                              args[(*pcd)++] = (short)side2;
                              args[(*pcd)++] = ref;
                              args[(*pcd)++] = rp;
                              args[(*pcd)++] = w_storage;
                              break;

                           case at_relative2:
                              args[(*pcd)++] = TTFUN_RELATIVE2V;
                              args[(*pcd)++] = (short)real_side1;
                              args[(*pcd)++] = (short)real_side2;
                              args[(*pcd)++] = (short)side1;
                              args[(*pcd)++] = (short)side2;
                              args[(*pcd)++] = ref;
                              args[(*pcd)++] = rp;
                              args[(*pcd)++] = w_storage;
                              break;

                           case at_side1:
                           case at_side2:
                              LogError(MSG_WARNING, MSG_ALIGN, NULL);
                              break;
                        }
                     }

                     return status;
} 




/***
** Function: EmitHStem
**
** Description:
**   This function generates the code that
**   will create and grid fit points in the
**   twilight zone, corresponding to a hstem.
***/
errcode EmitHStem(UBYTE *pgm, short *pc,
                  short *args, short *pcd,
                  struct T1Metrics *t1m,
                  const funit width,
                  const funit side1,
                  const funit side2,
                  const short rp,
                  const enum aligntype align,
                  const short ref)
{
   errcode status = SUCCESS;
   short w_storage;

   if ((w_storage = GetHStemWidth(GetWeight(t1m), width))==NOMEM) {
      SetError(status = NOMEM);
   } else {

      pgm[(*pc)++] = op_call;
      switch (align) {

         case at_side1:
            args[(*pcd)++] = TTFUN_SIDE1;
            args[(*pcd)++] = (short)side1;
            args[(*pcd)++] = (short)side2;
            args[(*pcd)++] = ref;
            args[(*pcd)++] = rp;
            args[(*pcd)++] = w_storage;
            break;

         case at_side2:
            args[(*pcd)++] = TTFUN_SIDE2;
            args[(*pcd)++] = (short)side1;
            args[(*pcd)++] = (short)side2;
            args[(*pcd)++] = ref;
            args[(*pcd)++] = rp;
            args[(*pcd)++] = w_storage;
            break;

         case at_relative1:
            args[(*pcd)++] = TTFUN_RELATIVE1H;
            args[(*pcd)++] = (short)side1;
            args[(*pcd)++] = (short)side2;
            args[(*pcd)++] = ref;
            args[(*pcd)++] = rp;
            args[(*pcd)++] = w_storage;
            break;

         case at_relative2:
            args[(*pcd)++] = TTFUN_RELATIVE2H;
            args[(*pcd)++] = (short)side1;
            args[(*pcd)++] = (short)side2;
            args[(*pcd)++] = ref;
            args[(*pcd)++] = rp;
            args[(*pcd)++] = w_storage;
            break;

         case at_centered:
         default:
            args[(*pcd)++] = TTFUN_HCENTER;
            args[(*pcd)++] = (short)side1;
            args[(*pcd)++] = (short)side2;
            args[(*pcd)++] = (short)((side1+side2)/2);
            args[(*pcd)++] = rp;
            args[(*pcd)++] = w_storage;
            break;
      }
   }

   return status;
}






/***
** Function: FamilyCutIn
**
** Description:
**   This function generates a branch in the
**   pre-program. 
***/
USHORT FamilyCutIn(UBYTE *prep,
                   USHORT tp,
                   const short cis)
{
   prep[tp++] = op_mppem;
   if (cis<256) {
      prep[tp++] = op_pushb1; prep[tp++] = (UBYTE)cis;
   } else {
      prep[tp++] = op_pushw1;
      prep[tp++] = HIBYTE(cis);
      prep[tp++] = LOBYTE(cis);
   }
   prep[tp++] = op_lt;
   prep[tp++] = op_if;

   return tp;
}




/***
** Function: SetProjection
**
** Description:
**   This function generates the TrueType code that
**   changes the projection vector in oblique typefaces.
***/
void SetProjection(UBYTE *pgm, short *pc,
                   short *args, short *pcd,
                   const funit x, const funit y)
{
   pgm[(*pc)++] = op_call;
   args[(*pcd)++] = TTFUN_OBLIQUE;
   args[(*pcd)++] = (short)y;
   args[(*pcd)++] = (short)x;
}


/***
** Function: AssembleArgs
**
** Description:
**   This function takes a sequence of arguments and
**   assembles them into a sequence of PUSHB1[], PUSHW1[],
**   NPUSHB[] and NPUSHW[] instructions.
***/
void AssembleArgs(short *args, const short pcd, UBYTE *is, short *cnt)
{
   short bytes;
   short i,j;


   if ((args[pcd-1] <= UCHAR_MAX && args[pcd-1]>=0)) {
      bytes = 1;
   } else {
      bytes = 0;
   }

   for (i=0, j=0; j<pcd; i++) {

      /* Pack a sequence of bytes? */
      if (bytes) {
         if ((i-j)>=255 || i==pcd ||
             (args[pcd-i-1]>UCHAR_MAX || args[pcd-i-1]<0)) {
            bytes = 0;
            if ((i-j)<=8) {
               is[(*cnt)++] = (UBYTE)(op_pushb1 + (i-j) - 1);
            } else {
               is[(*cnt)++] = op_npushb;
               is[(*cnt)++] = (UBYTE)(i-j);
            }
            while (j<i)
               is[(*cnt)++] = (UBYTE)args[pcd-1-j++];
         }

         /* Pack a sequence of words? */
      } else {
         if ((i-j)>=255 || i==pcd || 
             (args[pcd-i-1]<=UCHAR_MAX && args[pcd-i-1]>=0)) {
            bytes = 1;
            if ((i-j)<=8) {
               is[(*cnt)++] = (UBYTE)(op_pushw1 + (i-j) - 1);
            } else {
               is[(*cnt)++] = op_npushw;
               is[(*cnt)++] = (UBYTE)(i-j);
            }
            while (j<i) {
               is[(*cnt)++] = HIBYTE(args[pcd-j-1]);
               is[(*cnt)++] = LOBYTE(args[pcd-j-1]);
               j++;
            }
         }
      }
   }
}