/*
 * ttf2tfm.c
 *
 * This file is part of the ttf2pk package.
 *
 *    Loyer Frederic <loyer@ensta.fr>
 */

/*
 *   This program converts TTF files to TeX TFM files, and optionally
 *   to TeX VPL files that retain all kerning and ligature information.
 *   Both files make the characters not normally encoded by TeX available
 *   by character codes greater than 127.
 */

/*
 *   Adapted from afm2tfm by F.Loyer <loyer@ensta.fr>
 *   Following comments are from afm2tfm.
 */

/*   (Modified by Don Knuth from Tom Rokicki's pre-VPL version.) */
/*   VM/CMS port by J. Hafner (hafner@almaden.ibm.com), based on
 *   the port by Alessio Guglielmi (guglielmi@ipisnsib.bitnet)
 *   and Marco Prevedelli (prevedelli@ipisnsva.bitnet).
 *   This port is still in test state.  No guarantees.
 *   11/3/92: more corrections to VM/CMS port.  Now it looks correct
 *   and will be supported by J. Hafner.
 *
*/

#include <stdio.h>

#include <kpathsea/kpathsea.h>

#include "freetype.h"
#include "tterror.h"            /* for Panic() and Message() only */
#include "extend/ftxkern.h"
#include "enclib.h"

#define Macintosh_platform 1
#define Macintosh_encoding 0
#define Microsoft_platform 3
#define Microsoft_Symbol_encoding 0
#define Microsoft_Unicode_encoding 1

#define WRITEBIN FOPEN_WBIN_MODE

/* JLH: added these to make the code easier to read and remove some
   ascii<->ebcdic dependencies */
#define ASCII_A 65
#define ASCII_Z 90
#define ASCII_a 97
#define ASCII_z 122
#define ASCII_0 48
#define ASCII_9 57

#ifdef VMCMS
#define interesting lookstr  /* for 8 character truncation conflicts */
#include "dvipscms.h"
extern FILE *cmsfopen() ;
extern char ebcdic2ascii[] ;
extern char ascii2ebcdic[] ;
#ifdef fopen
#undef fopen
#endif
#define fopen cmsfopen
#endif

#if (defined(MSDOS) && defined(__TURBOC__)) || \
    (defined(OS2) && defined(_MSC_VER))
#define SMALLMALLOC
#endif

struct encoding
{
  char *name;
  char *vec[256];
};

struct encoding staticencoding =
{
  "TeX text",
  {"Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma",
   "Upsilon", "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle",
   "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", "acute",
   "caron", "breve", "macron", "ring", "cedilla", "germandbls", "ae", "oe",
   "oslash", "AE", "OE", "Oslash", "space", "exclam", "quotedbl", "numbersign",
   "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright",
   "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one",
   "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon",
   "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C",
   "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
   "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash",
   "bracketright", "circumflex", "underscore", "quoteleft", "a", "b", "c", "d",
   "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
   "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
   "tilde", "dieresis", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", ""
  }
};


/*
 *   It's easier to put this in static storage and parse it as we go
 *   than to build the structures ourselves.
 *
 *   The semicolons in the LIGKERN lines must be left isolated.
 */
char *staticligkern[] =
{
  "% LIGKERN space l =: lslash ; space L =: Lslash ;",
  "% LIGKERN question quoteleft =: questiondown ;",
  "% LIGKERN exclam quoteleft =: exclamdown ;",
  "% LIGKERN hyphen hyphen =: endash ; endash hyphen =: emdash ;",
  "% LIGKERN quoteleft quoteleft =: quotedblleft ;",
  "% LIGKERN quoteright quoteright =: quotedblright ;",
  "% LIGKERN space {} * ; * {} space ; zero {} * ; * {} zero ;",
  "% LIGKERN one {} * ; * {} one ; two {} * ; * {} two ;",
  "% LIGKERN three {} * ; * {} three ; four {} * ; * {} four ;",
  "% LIGKERN five {} * ; * {} five ; six {} * ; * {} six ;",
  "% LIGKERN seven {} * ; * {} seven ; eight {} * ; * {} eight ;",
  "% LIGKERN nine {} * ; * {} nine ;",

  /* Kern accented characters the same way as their base. */

  "% LIGKERN Aacute <> A ; aacute <> a ;",
  "% LIGKERN Acircumflex <> A ; acircumflex <> a ;",
  "% LIGKERN Adieresis <> A ; adieresis <> a ;",
  "% LIGKERN Agrave <> A ; agrave <> a ;",
  "% LIGKERN Aring <> A ; aring <> a ;",
  "% LIGKERN Atilde <> A ; atilde <> a ;",
  "% LIGKERN Ccedilla <> C ; ccedilla <> c ;",
  "% LIGKERN Eacute <> E ; eacute <> e ;",
  "% LIGKERN Ecircumflex <> E ; ecircumflex <> e ;",
  "% LIGKERN Edieresis <> E ; edieresis <> e ;",
  "% LIGKERN Egrave <> E ; egrave <> e ;",
  "% LIGKERN Iacute <> I ; iacute <> i ;",
  "% LIGKERN Icircumflex <> I ; icircumflex <> i ;",
  "% LIGKERN Idieresis <> I ; idieresis <> i ;",
  "% LIGKERN Igrave <> I ; igrave <> i ;",
  "% LIGKERN Ntilde <> N ; ntilde <> n ;",
  "% LIGKERN Oacute <> O ; oacute <> o ;",
  "% LIGKERN Ocircumflex <> O ; ocircumflex <> o ;",
  "% LIGKERN Odieresis <> O ; odieresis <> o ;",
  "% LIGKERN Ograve <> O ; ograve <> o ;",
  "% LIGKERN Oslash <> O ; oslash <> o ;",
  "% LIGKERN Otilde <> O ; otilde <> o ;",
  "% LIGKERN Scaron <> S ; scaron <> s ;",
  "% LIGKERN Uacute <> U ; uacute <> u ;",
  "% LIGKERN Ucircumflex <> U ; ucircumflex <> u ;",
  "% LIGKERN Udieresis <> U ; udieresis <> u ;",
  "% LIGKERN Ugrave <> U ; ugrave <> u ;",
  "% LIGKERN Yacute <> Y ; yacute <> y ;",
  "% LIGKERN Ydieresis <> Y ; ydieresis <> y ;",
  "% LIGKERN Zcaron <> Z ; zcaron <> z ;",

  /*
   *   These next are only included for deficient afm files that
   *   have the lig characters but not the lig commands.
   */

  "% LIGKERN f i =: fi ; f l =: fl ; f f =: ff ; ff i =: ffi ;",
  "% LIGKERN ff l =: ffl ;",
  0
};


/*
 *   The above layout corresponds to TeX Typewriter Type and is compatible
 *   with TeX Text because the position of ligatures is immaterial.
 */

struct encoding *outencoding = 0;
struct encoding *inencoding = 0;
char *outenname, *inenname;
                        /* the file names for input and output encodings */

int boundarychar = -1;     /* the boundary character */
int ignoreligkern;         /* do we look at ligkern info in the encoding? */


/*
 *   This is what we store Adobe data in.
 */

struct adobeinfo
{
  struct adobeinfo *next;
  int adobenum, texnum, width;
  int glyphnum;
  char *adobename;

  int llx, lly, urx, ury;

  struct lig *ligs;
  struct kern *kerns;
  struct adobeptr *kern_equivs;
  struct pcc *pccs;

  int wptr, hptr, dptr, iptr;
} *adobechars, *adobeptrs[256], *texptrs[256],
  *uppercase[256], *lowercase[256];

int nexttex[256];   /* for characters encoded multiple times in output */


/*
 *   These are the eight ligature ops, in VPL terms and in METAFONT terms.
 */

char *vplligops[] =
{
  "LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/", "/LIG/>", "/LIG/>>", 0
};

char *encligops[] =
{
  "=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>", 0
};


struct lig
{
  struct lig *next;
  char *succ, *sub;
  short op, boundleft;
};

struct kern
{
  struct kern *next;
  char *succ;
  int delta;
};

struct adobeptr
{
  struct adobeptr *next;
  struct adobeinfo *ch;
};

struct pcc
{
  struct pcc *next;
  char * partname;
  int xoffset, yoffset;
};


FILE *ttfin, *afmin, *vplout, *tfmout;

char inname[200], outname[200],
     *real_inname;  /* names of input and output files */

#define BUF_SIZE 255

char buffer[BUF_SIZE];   /* input buffer (modified while parsing) */
char obuffer[BUF_SIZE];  /* unmodified copy of input buffer */

char *param;        /* current position in input buffer */
char *fontname = "Unknown";
char *codingscheme = "Unspecified";

#ifdef VMCMS
char *ebfontname;
char *ebcodingscheme;
#endif

int units_per_em;
float italicangle = 0.0;
char fixedpitch;
char makevpl;
char pedantic;
char quiet;
int xheight = 400;
int fontspace;
int bc, ec;
long cksum;
float efactor = 1.0, slant = 0.0;
float capheight = 0.8;
char *efactorparam, *slantparam;
double newslant;
char titlebuf[500];



int
transform(x, y)
  register int x, y;
{
  register double acc;


  acc = efactor * x + slant * y;
  return (int)(acc >= 0 ? floor(acc+0.5) : ceil(acc-0.5));
}


/* getline() will truncate input lines after BUF_SIZE characters */

int
getline()
{
  register char *p;
  register int c;
  register int count;


  param = buffer;
  for (p = buffer, count = 0;
       (c = getc(afmin)) != EOF && c != '\n' && count < BUF_SIZE - 1; count++)
    *p++ = c;
  *p = 0;

  (void)strcpy(obuffer, buffer);

  if (p == buffer && c == EOF)
    return 0;
  else
    return 1;
}


char *
mymalloc(len)
  unsigned long len;
{
  register char *p;
  int i;


#ifdef SMALLMALLOC
  if (len > 65500L)
    Panic("ERROR: Can't allocate more than 64kByte\n");
#endif

  p = malloc((unsigned)len);
  if (p == NULL)
    Panic("ERROR: Out of memory\n");

  for (i = 0; i < len; i++)
    p[i] = 0;
  return p;
}


char *
newstring(s)
  char *s;
{
  char *q = mymalloc((unsigned long)(strlen(s) + 1));
  (void)strcpy(q, s);
  return q;
}


char *
paramnewstring()
{
  register char *p, *q;


  p = param;
  while (*p > ' ')
    p++;
  if (*p != 0)
    *p++ = 0;
  q = newstring(param);
  while (*p && *p <= ' ')
    p++;
  param = p;
  return q;
}


char *
paramstring()
{
  register char *p, *q;


  p = param;
  while (*p > ' ')
    p++;
  q = param;
  if (*p != 0)
    *p++ = 0;
  while (*p && *p <= ' ')
    p++;
  param = p;
  return q;
}


int
paramnum()
{
  register char *p;
  int i;


  p = paramstring();
  if (sscanf(p, "%d", &i) != 1)
    Panic("ERROR: Integer expected\n");
  return i;
}


float
paramfloat()
{
  register char *p;
  float i;

  p = paramstring();
  if (sscanf(p, "%f", &i) != 1)
    Panic("ERROR: Number expected\n");
  return i;
}


struct adobeinfo *
newchar()
{
  register struct adobeinfo *ai;


  ai = (struct adobeinfo *)mymalloc((unsigned long)sizeof (struct adobeinfo));
  ai->adobenum = -1;
  ai->texnum = -1;
  ai->glyphnum = -1;
  ai->width = -1;
  ai->adobename = NULL;
  ai->llx = -1;
  ai->lly = -1;
  ai->urx = -1;
  ai->ury = -1;
  ai->ligs = NULL;
  ai->kerns = NULL;
  ai->kern_equivs = NULL;
  ai->pccs = NULL;
  ai->next = adobechars;
  adobechars = ai;

  return ai;
}


struct kern *
newkern()
{
  register struct kern *nk;


  nk = (struct kern *)mymalloc((unsigned long)sizeof (struct kern));
  nk->next = NULL;
  nk->succ = NULL;
  nk->delta = 0;

  return nk;
}


struct pcc *
newpcc()
{
  register struct pcc *np;


  np = (struct pcc *)mymalloc((unsigned long)sizeof (struct pcc));
  np->next = NULL;
  np->partname = NULL;
  np->xoffset = 0;
  np->yoffset = 0;

  return np;
}


struct lig *
newlig()
{
  register struct lig *nl;


  nl = (struct lig *)mymalloc((unsigned long)sizeof (struct lig));
  nl->next = NULL;
  nl->succ = NULL;
  nl->sub = NULL;
  nl->op = 0; /* the default =: op */
  nl->boundleft = 0;

  return nl;
}


struct adobeinfo *
findglyph(g)
  int g;
{
  register struct adobeinfo *ai;


  for (ai = adobechars; ai; ai = ai->next)
   if (g == ai->glyphnum)
     return ai;

  return NULL;
}


struct adobeinfo *
findadobe(p)
  char *p;
{
  register struct adobeinfo *ai;


  for (ai = adobechars; ai; ai = ai->next)
    if (strcmp(p, ai->adobename) == 0)
      return ai;

  return NULL;
}


struct encoding *readencoding();


void
makeaccentligs()
{
  register struct adobeinfo *ai, *aci;
  register char *p;
  register struct lig *nl;


  for (ai = adobechars; ai; ai = ai->next)
  {
    p = ai->adobename;
    if (strlen(p) > 2)
      if ((aci = findadobe(p+1)) && (aci->adobenum > 127))
      {
        nl = newlig();
        nl->succ = mymalloc((unsigned long)2);
        *(nl->succ + 1) = 0;
        *(nl->succ) = *p;
        nl->sub = ai->adobename;
        nl->next = aci->ligs;
        aci->ligs = nl;
      }
  }
}


TT_Engine   engine;
TT_Face     face;
TT_Instance instance;
TT_Glyph    glyph;
TT_CharMap  char_map;

TT_Glyph_Metrics    metrics;
TT_Face_Properties  properties;
TT_Instance_Metrics imetrics;

TT_Kerning directory;


int
readttf_kern()
{
  register struct kern *nk;
  register struct adobeinfo *ai;
  TT_Kern_0_Pair* pairs0;
  TT_Error error;
  int i, j;

  
  error = TT_Get_Kerning_Directory(face, &directory);
  
  if (directory.nTables == 0)
    return 0;

  for (i = 0; i < directory.nTables; i++)
  {
    error = TT_Load_Kerning_Table(face, i);

    switch (directory.tables->format)
    {
    case 0:
      pairs0 = directory.tables->t.kern0.pairs;
      for (j = 0; j < directory.tables->t.kern0.nPairs; j++, pairs0++)
      {
        ai = findglyph(pairs0->left);
        if (ai == NULL)
          Message("kern char not found\n");
        else
        {
          nk = newkern();
          nk->succ = findglyph(pairs0->right)->adobename;
          nk->delta = transform(pairs0->value*1000/units_per_em, 0);
          nk->next = ai->kerns;
          ai->kerns = nk;
        }
      }
      break;

    default:
      break;
    }
  }
  return 1;
}


void
readttf()
{
  TT_Error error;
  struct adobeinfo *ai;
  int Num;
  int i;
  int num_cmap;
  short cmap_plat, cmap_enc;


  /*
   *   We allocate a placeholder boundary char.
   */

  ai = newchar();
  ai->adobenum = -1;
  ai->adobename = "||"; /* boundary character name */

  /* Initialize FreeType engine */

  if ((error = TT_Init_FreeType(&engine)))
    Panic("ERROR: While initializing engine, error code = %d\n", error);

  if ((error = TT_Init_Kerning_Extension(engine)))
    Panic("ERROR: While initializing kerning, error code = %d\n", error);

  /* Load face */

  error = TT_Open_Face(engine, real_inname, &face);
  if (error)
    Panic("ERROR: Could not open %s\n", real_inname);

  /* create instance */

  error = TT_New_Instance(face, &instance);
  if (error)
    Panic("ERROR: Could not create instance for %s\n", real_inname);

  /* we use a dummy glyph size of 10pt */

  error = TT_Set_Instance_CharSize(instance, 10 * 64);
  if (error)
    Panic("ERROR: Could not set character set\n");

  /* get face properties and allocate preload arrays */
  
  TT_Get_Face_Properties(face, &properties);

  /* create glyph */

  error = TT_New_Glyph(face, &glyph);
  if (error)
    Panic("ERROR: Could not create glyph container\n");


  units_per_em = properties.header->Units_Per_EM;
  fixedpitch = 0;
  italicangle = 0;

  num_cmap = TT_Get_CharMap_Count(face);
  for (i = 0; i < num_cmap; i++)
  {
    error = TT_Get_CharMap_ID(face, i, &cmap_plat, &cmap_enc);
    if (error)
      Panic("ERROR: Cannot query cmap, error code = %d\n", error);
    if (cmap_plat == Microsoft_platform &&
        cmap_enc == Microsoft_Unicode_encoding)
    {
      set_encoding_scheme(encUnicode);
      break;
    }
  }
  if (i == num_cmap)
    Panic("ERROR: Invalid platform and/or encoding ID\n");

  error = TT_Get_CharMap(face, i, &char_map);
  if (error)
    Panic("ERROR: Cannot load cmap, error code = %d\n", error);

  /*
    TODO: supporting the folowing encoding... (unicode is
    preferred):

    Microsoft_platform/Microsoft_Symbol_encoding
      set_encoding_scheme(encFontSpecific);
      codingscheme= "FontSpecific";

    Macintosh_platform/Macintosh_encoding
      set_encoding_scheme(encMac);
      codingscheme= "MacEncoding";
   */

  for (i = 0; i < 65536; i++)
  {
    Num = TT_Char_Index(char_map, i);
          
    if (Num < 0)
      Panic("ERROR: Failure on cmap mapping from %s\n", inname);
    if (Num == 0)
      continue;
    error = TT_Load_Glyph(instance, glyph, Num, 0);
    if (!error)
      error = TT_Get_Glyph_Metrics(glyph, &metrics);

    if (!error)
    {       
      ai = newchar();
      ai->adobenum = i;
      ai->glyphnum = Num;
      ai->adobename = code_to_adobename(i);
      ai->llx = metrics.bbox.xMin *1000/units_per_em;
      ai->lly = metrics.bbox.yMin *1000/units_per_em;
      ai->llx = transform(ai->llx, ai->lly);
      ai->urx = metrics.bbox.xMax *1000/units_per_em;
      ai->ury = metrics.bbox.yMax *1000/units_per_em;
      ai->urx = transform(ai->urx, ai->ury);
      /* We need to avoid negative heights or depths.  They break accents in
         math mode, among other things.  */
      if (ai->lly > 0)
        ai->lly = 0;
      if (ai->ury < 0)
        ai->ury = 0;
      ai->width = transform(metrics.advance * 1000 / units_per_em, 0);

      if (!quiet)
        printf("%5d  %04x  %-25s %5d  % 5d,% 5d -- % 5d,% 5d\n",
               Num, i, ai->adobename,
               ai->width,
               ai->llx, ai->lly, ai->ury, ai->urx);

      if (ai->adobenum >= 0 && ai->adobenum < 256)
        adobeptrs[ai->adobenum] = ai;
    }
  }
  readttf_kern();
  /* CLOSE Font */
}


/*
 *   Re-encode the adobe font.  Assumes that the header file will
 *   also contain the appropriate instructions!
 */

void
handlereencoding()
{
  if (inenname)
  {
    int i;
    struct adobeinfo *ai;
    char *p;


    ignoreligkern = 1;
    inencoding = readencoding(inenname);
    for (i = 0; i < 256; i++)
      if (0 != (ai = adobeptrs[i]))
      {
        ai->adobenum = -1;
        adobeptrs[i] = NULL;
      }
    for (i = 0; i < 256; i++)
    {
      p = inencoding->vec[i];
      if (p && *p && 0 != (ai = findadobe(p)))
      {
        ai->adobenum = i;
        adobeptrs[i] = ai;
      }
    }
    codingscheme = inencoding->name;
  }

  ignoreligkern = 0;
  if (outenname)
    outencoding = readencoding(outenname);
  else
    outencoding = readencoding((char *)0);
}


/*
 *   This routine reverses a list.  We use it because we accumulate the
 *   adobeinfo list in reverse order, but when we go to map the
 *   characters, we would prefer to use the original ordering.  It just
 *   makes more sense.
 */

struct adobeinfo *
revlist(p)
  struct adobeinfo *p;
{
  struct adobeinfo *q = 0, *t;


  while (p)
  {
    t = p->next;
    p->next = q;
    q = p;
    p = t;
  }
  return q;
}


void
assignchars()
{
  register char **p;
  register int i, j;
  register struct adobeinfo *ai, *pai;
  int nextfree = 128;
  struct pcc *pcp;


  /*
   *   First, we assign all those that match perfectly.
   */

  for (i = 0, p = outencoding->vec; i < 256; i++, p++)
    if ((ai = findadobe(*p)) && (ai->adobenum >= 0 || ai->pccs != NULL))
    {
      if (ai->texnum >= 0)
        nexttex[i] = ai->texnum; /* linked list */
      ai->texnum = i;
      texptrs[i] = ai;
    }

  if (pedantic)
    return;

  /*
   *   Next, we assign all the others, retaining the adobe positions, possibly
   *   multiplying assigning characters, unless the output encoding was
   *   precisely specified.
   */

  for (ai = adobechars; ai; ai = ai->next)
    if (ai->adobenum >= 0 && ai->adobenum < 256 &&
        ai->texnum < 0 && texptrs[ai->adobenum] == 0)
    {
      ai->texnum = ai->adobenum;
      texptrs[ai->adobenum] = ai;
    }

  /*
  *   Finally, we map all remaining characters into free locations beginning
  *   with 128, if we know how to construct those characters.  We need to
  *   make sure the component pieces are mapped.
  */

  adobechars = revlist(adobechars);
  for (ai = adobechars; ai; ai = ai->next)
    if (ai->texnum < 0 && (ai->adobenum >= 0 || ai->pccs != NULL))
    {
      while (texptrs[nextfree])
      {
        nextfree = (nextfree + 1) & 255;
        if (nextfree == 128)
          goto finishup;        /* all slots full */
      }
      ai->texnum = nextfree;
      texptrs[nextfree] = ai;
    }

finishup:
  /*
   *   We now check all of the composite characters.  If any of the
   *   components are not mapped, we unmap the composite character.
   */

  for (i = 0; i < 256; i++)
  {
    ai = texptrs[i];
    if (ai && ai->pccs != NULL && ai->texnum >= 0)
    {
      for (pcp = ai->pccs; pcp; pcp = pcp->next)
      {
        pai = findadobe(pcp->partname);
        if (pai == NULL || pai->texnum < 0)
        {
          texptrs[ai->texnum] = 0;
          ai->texnum = -1;
          break;
        }
      }
    }
  }

  /*
   *   Now, if any of the characters are encoded multiple times, we want
   *   ai->texnum to be the first one assigned, since that is most likely
   *   to be the most important one.  So we reverse the above lists.
   */

  for (ai = adobechars; ai; ai = ai->next)
    if (ai->texnum >= 0 && ai->texnum < 256)
    {
      j = -1;
      while (nexttex[ai->texnum] >= 0)
      {
        i = nexttex[ai->texnum];
        nexttex[ai->texnum] = j;
        j = ai->texnum;
        ai->texnum = i;
      }
      nexttex[ai->texnum] = j;
    }
}


/* Compute uppercase mapping, when making a small caps font. */

void
upmap()
{
  register struct adobeinfo *ai, *Ai;
  register char *p, *q;
  register struct pcc *np, *nq;
  int i;
  char lwr[50];


  /* JLH: changed some lines below to be ascii<->ebcdic independent
          any reason we don't use 'isupper'?.
     Looks like we should use isupper to me -- karl.  */

  for (Ai = adobechars; Ai; Ai = Ai->next)
  {
    p = Ai->adobename;
    if (isupper (*p))
    {
      q = lwr;
      for (; *p; p++)
        *q++ = TOLOWER(*p);
      *q = '\0';   /* changed this too! */

      if (0 != (ai = findadobe(lwr)))
      {
        for (i = ai->texnum; i >= 0; i = nexttex[i])
          uppercase[i] = Ai;
        for (i = Ai->texnum; i >= 0; i = nexttex[i])
          lowercase[i] = ai;
      }
    }
  }

  /* Note that, contrary to the normal true/false conventions,
   * uppercase[i] is NULL and lowercase[i] is non-NULL when i is the
   * ASCII code of an uppercase letter; and vice versa for lowercase 
   * letters.
   */

  if (0 != (ai = findadobe("germandbls")))
    if (0 != (Ai = findadobe("S")))     /* we also construct SS */
    {
      for (i = ai->texnum; i >= 0; i = nexttex[i])
        uppercase[i] = ai;
      ai->adobenum = -1;
      ai->width = Ai->width << 1;
      ai->llx = Ai->llx;
      ai->lly = Ai->lly;
      ai->urx = Ai->width + Ai->urx;
      ai->ury = Ai->ury;
      ai->kerns = Ai->kerns;

      np = newpcc();
      np->partname = "S";
      nq = newpcc();
      nq->partname = "S";
      nq->xoffset = Ai->width;
      np->next = nq;
      ai->pccs = np;
    }

  if ((ai = findadobe("dotlessi")))
    for (i = ai->texnum; i >= 0; i = nexttex[i])
      uppercase[i] = findadobe("I");

  if ((ai = findadobe("dotlessj")))
    for (i = ai->texnum; i >= 0; i = nexttex[i])
      uppercase[i] = findadobe("J");
}

/* The logic above seems to work well enough, but it leaves useless characters
 * like `fi' and `fl' in the font if they were present initially,
 * and it omits characters like `dotlessj' if they are absent initially. */



/* Now we turn to computing the TFM file */

int lf, lh, nw, nh, nd, ni, nl, nk, ne, np;


void
write16(what)
  register short what;
{
  (void)fputc(what >> 8, tfmout);
  (void)fputc(what & 255, tfmout);
}


void
writearr(p, n)
  register long *p;
  register int n;
{
  while (n)
  {
    write16((short)(*p >> 16));
    write16((short)(*p & 65535));
    p++;
    n--;
  }
}


void
makebcpl(p, s, n)
  register long *p;
  register char *s;
  register int n;
{
  register long t;
  register long sc;


  if (strlen(s) < n)
    n = strlen(s);
  t = ((long)n) << 24;
  sc = 16;

  while (n > 0)
  {
    t |= ((long)(*(unsigned char *)s++)) << sc;
    sc -= 8;
    if (sc < 0)
    {
      *p++ = t;
      t = 0;
      sc = 24;
    }
    n--;
  }
  *p++ = t;
}


int source[257];
int unsort[257];

/*
 *   Next we need a routine to reduce the number of distinct dimensions
 *   in a TFM file.  Given an array what[0]..what[oldn-1], we want to
 *   group its elements into newn clusters, in such a way that the maximum
 *   difference between elements of a cluster is as small as possible.
 *   Furthermore, what[0]=0, and this value must remain in a cluster by
 *   itself.  Data such as `0 4 6 7 9' with newn=3 shows that an iterative
 *   scheme in which 6 is first clustered with 7 will not work.  So we
 *   borrow a neat algorithm from METAFONT to find the true optimum.
 *   Memory location what[oldn] is set to 0x7fffffffL for convenience.
 */

long nextd; /* smallest value that will give a different mincover */


int
mincover(what, d) /* tells how many clusters result, given max difference d */
  register long d;
  long *what;
{
  register int m;
  register long l;
  register long *p;


  nextd = 0x7fffffffL;
  p = what+1;
  m = 1;

  while (*p < 0x7fffffffL)
  {
    m++;
    l = *p;
    while (*++p <= l + d)
      ;
    if (*p - l < nextd)
      nextd = *p - l;
  }
  return m;
}


void
remap(what, oldn, newn)
  long *what;
  int oldn, newn;
{
  register int i, j;
  register long d, l;


  what[oldn] = 0x7fffffffL;
  for (i = oldn-1; i > 0; i--)
  {
    d = what[i];
    for (j = i; what[j+1] < d; j++)
    {
      what[j] = what[j+1];
      source[j] = source[j+1];
    }
    what[j] = d;
    source[j] = i;
  } /* Tom, don't let me ever catch you using bubblesort again! -- Don */

  i = mincover(what, 0L);
  d = nextd;
  while (mincover(what, d + d) > newn)
    d += d;
  while (mincover(what, d) > newn)
    d = nextd;

  i = 1;
  j = 0;
  while (i < oldn)
  {
    j++;
    l = what[i];
    unsort[source[i]] = j;
    while (what[++i] <= l + d)
    {
      unsort[source[i]] = j;
      if (i - j == oldn - newn)
        d = 0;
    }
    what[j] = (l + what[i-1])/2;
  }
}


long
checksum()
{
  int i;
  unsigned long s1 = 0, s2 = 0;
  char *p;
  struct adobeinfo *ai;


  for (i = 0; i < 256; i++)
    if (0 != (ai=adobeptrs[i]))
    {
      s1 = ((s1 << 1) ^ (s1 >> 31)) ^ ai->width; /* cyclic left shift */
      s1 &= 0xffffffff; /* in case we're on a 64-bit machine */

      for (p = ai->adobename; *p; p++)
#ifndef VMCMS
        s2 = (s2 * 3) + *p;
#else
        s2 = (s2 * 3) + ebcdic2ascii[*p];
#endif
    }

  s1 = (s1 << 1) ^ s2;
  return s1;
}


/*
 *   The next routine simply scales something.
 *   Input is in units per em.  Output is in FIXFACTORths of units per em.
 */

#define FIXFACTOR (0x100000L)   /* 2^{20}, the unit fixnum */

long
scale(what)
  long what;
{
  return ((what / units_per_em) << 20) +
         (((what % units_per_em) << 20) + units_per_em / 2) / units_per_em;
}


long *header, *charinfo,
     *width, *height, *depth,
     *ligkern, *kern, *tparam,
     *italic;
long *tfmdata;


void
buildtfm()
{
  register int i, j;
  register struct adobeinfo *ai;


  header = tfmdata;
  cksum = checksum();
  header[0] = cksum;
  header[1] = 0xa00000; /* 10pt design size */
  makebcpl(header + 2, codingscheme, 39);
  makebcpl(header + 12, fontname, 19);
  lh = 17;
  charinfo = header + lh;

  for (i = 0; i < 256 && adobeptrs[i] == NULL; i++);
    bc = i;
  for (i = 255; i >= 0 && adobeptrs[i] == NULL; i--);
    ec = i;
  if (ec < bc)
    Panic("ERROR: No Adobe characters\n");

  width = charinfo + (ec - bc + 1);
  width[0] = 0;
  nw++;
  for (i = bc; i <= ec; i++)
    if (0 != (ai = adobeptrs[i]))
    {
      width[nw] = ai->width;
      for (j = 1; width[j] != ai->width; j++)
        ;
      ai->wptr = j;
      if (j == nw)
        nw++;
    }
  if (nw > 256)
    Panic("ERROR: 256 chars with different widths\n");

  depth = width + nw;
  depth[0] = 0;
  nd = 1;
  for (i = bc; i <= ec; i++)
    if (0 != (ai = adobeptrs[i]))
    {
      depth[nd] = -ai->lly;
      for (j = 0; depth[j] != -ai->lly; j++)
        ;
      ai->dptr = j;
      if (j == nd)
        nd++;
    }

  if (nd > 16)
  {
    remap(depth, nd, 16);
    nd = 16;
    for (i = bc; i <= ec; i++)
      if (0 != (ai = adobeptrs[i]))
        ai->dptr = unsort[ai->dptr];
  }

  height = depth + nd;
  height[0] = 0;
  nh = 1;

  for (i = bc; i <= ec; i++)
    if (0 != (ai=adobeptrs[i]))
    {
      height[nh] = ai->ury;
      for (j = 0; height[j] != ai->ury; j++)
        ;
      ai->hptr = j;
      if (j == nh)
        nh++;
    }

  if (nh > 16)
  {
    remap(height, nh, 16);
    nh = 16;
    for (i = bc; i <= ec; i++)
      if (0 != (ai = adobeptrs[i]))
        ai->hptr = unsort[ai->hptr];
  }

  italic = height + nh;
  italic[0] = 0;
  ni = 1;

  for (i = bc; i <= ec; i++)
    if (0 != (ai=adobeptrs[i]))
    {
      italic[ni] = ai->urx - ai->width;
      if (italic[ni] < 0)
        italic[ni] = 0;
      for (j = 0; italic[j] != italic[ni]; j++)
        ;
      ai->iptr = j;
      if (j == ni)
        ni++;
    }

  if (ni > 64)
  {
    remap(italic, ni, 64);
    ni = 64;
    for (i = bc; i <= ec; i++)
      if (0 != (ai = adobeptrs[i]))
        ai->iptr = unsort[ai->iptr];
  }

  for (i = bc; i <= ec; i++)
    if (0 != (ai = adobeptrs[i]))
      charinfo[i-bc] = ((long)(ai->wptr)<<24) +
                       ((long)(ai->hptr)<<20) +
                       ((long)(ai->dptr)<<16) +
                       ((long)(ai->iptr)<<10);

  ligkern = italic + ni;
  nl = 0;           /* ligatures and kerns omitted from raw Adobe font */
  kern = ligkern + nl;
  nk = 0;

  newslant = (double)slant - efactor * tan(italicangle*(3.1415926535/180.0));
  tparam = kern + nk;
  tparam[0] = (long)(FIXFACTOR * newslant + 0.5);
  tparam[1] = scale((long)fontspace);
  tparam[2] = (fixedpitch ? 0 : scale((long)(300*efactor+0.5)));
  tparam[3] = (fixedpitch ? 0 : scale((long)(100*efactor+0.5)));
  tparam[4] = scale((long)xheight);
  tparam[5] = scale((long)(1000*efactor+0.5));
  np = 6;
}


void
writesarr(what, len)
  long *what;
  int len;
{
  register long *p;
  int i;


  p = what;
  i = len;
  while (i)
  {
    *p = scale(*p);
    (void)scale(*p); /* need this kludge for some compilers */
    p++;
    i--;
  }
  writearr(what, len);
}


void
writetfm()
{
  lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np;

  write16(lf);
  write16(lh);
  write16(bc);
  write16(ec);
  write16(nw);
  write16(nh);
  write16(nd);
  write16(ni);
  write16(nl);
  write16(nk);
  write16(ne);
  write16(np);
  writearr(header, lh);
  writearr(charinfo, ec - bc + 1);
  writesarr(width, nw);
  writesarr(height, nh);
  writesarr(depth, nd);
  writesarr(italic, ni);
  writearr(ligkern, nl);
  writesarr(kern, nk);
  writearr(tparam, np);
}



/* OK, the TFM file is done!  Now for our next trick, the VPL file. */

/* For TeX we want to compute a character height that works properly
 * with accents.  The following list of accents doesn't need to be
 * complete. */

/*
 *   We only do this if the xheight has a reasonable value.
 *   (>50)
 */

char *accents[] = {"acute", "tilde", "caron", "dieresis", NULL};


int
texheight(ai)
  register struct adobeinfo *ai;
{
  register char **p;
  register struct adobeinfo *aci, *acci;


  if (xheight <= 50 || *(ai->adobename + 1))
    return ai->ury;                     /* that was the simple case */

  for (p = accents; *p; p++)    /* otherwise we look for accented letters */
    if (0 != (aci = findadobe(*p)))
    {
      strcpy(buffer, ai->adobename);
      strcat(buffer, *p);
      if (0 != (acci = findadobe(buffer)))
        return acci->ury - aci->ury + xheight;
    }
  return ai->ury;
}


/* modified tgr to eliminate varargs problems */

#define vout(s) fprintf(vplout, s)

int level; /* the depth of parenthesis nesting in VPL file being written */


void
vlevout()
{
  register int l = level;


  while (l--)
    vout("   ");
}


void
vlevnlout()
{
  vout("\n");
  vlevout();
}

#define voutln(str)         {fprintf(vplout, "%s\n", str); vlevout();}
#define voutln2(f, s)       {fprintf(vplout, f, s); vlevnlout();}
#define voutln3(f, a, b)    {fprintf(vplout, f, a, b); vlevnlout();}
#define voutln4(f, a, b, c) {fprintf(vplout, f, a, b, c); vlevnlout();}


void
vleft()
{
  level++;
  vout("(");
}


void
vright()
{
  level--;
  voutln(")");
}


int forceoctal = 0;

char vcharbuf[6];


char *
vchar(c)
  int c;
{
  if (forceoctal == 0 && ISALNUM(c))
    (void)sprintf(vcharbuf, "C %c",
#ifndef VMCMS
     c);
#else
     ascii2ebcdic[c]);
#endif
  else
    (void)sprintf(vcharbuf, "O %o", (unsigned)c);
  return vcharbuf;
}


char vnamebuf[100];


char *
vname(c)
  int c;
{
  if (!forceoctal && ISALNUM(c))
    vnamebuf[0] = 0;
  else
    sprintf (vnamebuf, " (comment %s)", texptrs[c]->adobename);
  return vnamebuf;
}


void
writevpl()
{
  register int i, j, k;
  register struct adobeinfo *ai;
  register struct lig *nlig;
  register struct kern *nkern;
  register struct pcc *npcc;
  struct adobeinfo *asucc, *asub, *api;
  struct adobeptr *kern_eq;
  int xoff, yoff, ht;
  char unlabeled;


  voutln2("(VTITLE Created by %s)", titlebuf);
  voutln("(COMMENT Please edit that VTITLE if you edit this file)");
  (void)sprintf(obuffer, "TeX-%s%s%s%s",
                outname,
                (efactor == 1.0 ? "" : "-E"),
                (slant == 0.0 ? "" : "-S"),
                (makevpl == 1 ? "" : "-CSC"));

  if (strlen(obuffer) > 19) /* too long, will retain first 9 and last 10 */
  {
    register char *p, *q;


    for (p = &obuffer[9], q = &obuffer[strlen(obuffer)-10]; p < &obuffer[19];
         p++, q++)
      *p = *q;
    obuffer[19] = '\0';
  }
  voutln2("(FAMILY %s)", obuffer);

  {
    char tbuf[300];
    char *base_encoding = 
#ifndef VMCMS
      codingscheme;
#else
      ebcodingscheme;
#endif
      
    if (strcmp(outencoding->name, base_encoding) == 0)
      sprintf(tbuf, "%s", outencoding->name);
    else
      sprintf(tbuf, "%s + %s", base_encoding, outencoding->name);
      
    if (strlen(tbuf) > 39)
    {
      Message("Coding scheme too long; shortening to 39 characters.\n");
      tbuf[39] = 0;
    }
    voutln2("(CODINGSCHEME %s)", tbuf);
  }

  voutln("(DESIGNSIZE R 10.0)");
  voutln("(DESIGNUNITS R 1000)");
  voutln("(COMMENT DESIGNSIZE (1 em) IS IN POINTS)");
  voutln("(COMMENT OTHER DIMENSIONS ARE MULTIPLES OF DESIGNSIZE/1000)");

  /* Let vptovf compute the checksum. */
  /* voutln2("(CHECKSUM O %lo)", cksum ^ 0xffffffff); */

  if (boundarychar >= 0)
    voutln2("(BOUNDARYCHAR O %lo)", (unsigned long)boundarychar);

  vleft();
  voutln("FONTDIMEN");
  if (newslant)
    voutln2("(SLANT R %f)", newslant);
  voutln2("(SPACE D %d)", fontspace);
  if (!fixedpitch)
  {
    voutln2("(STRETCH D %d)", transform(200, 0));
    voutln2("(SHRINK D %d)", transform(100, 0));
  }
  voutln2("(XHEIGHT D %d)", xheight);
  voutln2("(QUAD D %d)", transform(1000, 0));
  voutln2("(EXTRASPACE D %d)", fixedpitch ? fontspace : transform(111, 0));
  vright();

  vleft();
  voutln("MAPFONT D 0");
  voutln2("(FONTNAME %s)", outname);
  /* voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum); */
  vright();

  if (makevpl > 1)
  {
    vleft();
    voutln("MAPFONT D 1");
    voutln2("(FONTNAME %s)", outname);
    voutln2("(FONTAT D %d)", (int)(1000.0*capheight+0.5));
    /* voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum); */
    vright();
  }

  for (i = 0; i < 256 && texptrs[i] == NULL; i++)
    ;
  bc = i;
  for (i = 255; i >= 0 && texptrs[i] == NULL; i--)
    ;
  ec = i;

  vleft();
  voutln("LIGTABLE");
  ai = findadobe("||");
  unlabeled = 1;
  for (nlig = ai->ligs; nlig; nlig = nlig->next)
    if (0 != (asucc = findadobe(nlig->succ)))
    {
      if (0 != (asub = findadobe(nlig->sub)))
        if (asucc->texnum >= 0)
          if (asub->texnum >= 0)
          {
            if (unlabeled)
            {
               voutln("(LABEL BOUNDARYCHAR)");
               unlabeled = 0;
            }
            for (j = asucc->texnum; j >= 0; j = nexttex[j])
              voutln4("(%s %s O %o)", vplligops[nlig->op],
                      vchar(j), (unsigned)asub->texnum);
          }
    }
  if (! unlabeled)
    voutln("(STOP)");

  for (i = bc; i <= ec; i++)
    if ((ai = texptrs[i]) && ai->texnum == i)
    {
      unlabeled = 1;
      if (uppercase[i] == NULL) /* omit ligatures from smallcap lowercase */
        for (nlig = ai->ligs; nlig; nlig = nlig->next)
          if (0 != (asucc = findadobe(nlig->succ)))
            if (0 != (asub = findadobe(nlig->sub)))
              if (asucc->texnum >= 0)
                if (asub->texnum >= 0)
                {
                  if (unlabeled)
                  {
                    for (j = ai->texnum; j >= 0; j = nexttex[j])
                      voutln3("(LABEL %s)%s", vchar(j), vname(j));
                    unlabeled = 0;
                  }
                  for (j = asucc->texnum; j >= 0; j = nexttex[j])
                  {
                    voutln4("(%s %s O %o)", vplligops[nlig->op],
                            vchar(j), (unsigned)asub->texnum);
                    if (nlig->boundleft)
                      break;
                  }
                }
 
      for (nkern = (uppercase[i] ? uppercase[i]->kerns : ai->kerns);
           nkern; nkern=nkern->next)
        if (0 != (asucc = findadobe(nkern->succ)))
          for (j = asucc->texnum; j >= 0; j = nexttex[j])
          {
            if (uppercase[j] == NULL)
            {
              if (unlabeled)
              {
                for (k = ai->texnum; k >= 0; k = nexttex[k])
                  voutln3("(LABEL %s)%s", vchar(k), vname(k));
                unlabeled = 0;
              }
              /* If other characters have the same kerns as this
                 one, output the label here.  This makes the TFM
                 file much smaller than if we output all the
                 kerns again under a different label.  */
              for (kern_eq = ai->kern_equivs; kern_eq;
                   kern_eq = kern_eq->next)
              {
                k = kern_eq->ch->texnum;
                if (k >= 0 && k < 256)
                  voutln3("(LABEL %s)%s", vchar(k), vname(k));
              }
              ai->kern_equivs = 0; /* Only output those labels once. */

              if (uppercase[i])
              {
                if (lowercase[j])
                {
                  for (k = lowercase[j]->texnum; k >= 0; k = nexttex[k])
                    voutln4("(KRN %s R %.1f)%s",
                            vchar(k), capheight * nkern->delta, vname(k));
                }
                else
                  voutln4("(KRN %s R %.1f)%s",
                          vchar(j), capheight * nkern->delta, vname(j));
              }
              else
              {
                voutln4("(KRN %s R %d)%s",
                        vchar(j), nkern->delta, vname(j));
                if (lowercase[j])
                  for (k = lowercase[j]->texnum; k >= 0; k = nexttex[k])
                    voutln4("(KRN %s R %.1f)%s",
                            vchar(k), capheight * nkern->delta, vname(k));
              }
            }
          }
          if (!unlabeled)
            voutln("(STOP)");
    }
  vright();

  for (i = bc; i <= ec; i++)
    if (0 != (ai = texptrs[i]))
    {
      vleft();
      fprintf(vplout, "CHARACTER %s\n   ", vchar(i));

      if (uppercase[i])
      {
        ai = uppercase[i];
        voutln2("(CHARWD R %.1f)", capheight * (ai->width));
        if (0 != (ht = texheight(ai)))
          voutln2("(CHARHT R %.1f)", capheight * ht);
        if (ai->lly)
          voutln2("(CHARDP R %.1f)", -capheight * ai->lly);
        if (ai->urx > ai->width)
          voutln2("(CHARIC R %.1f)", capheight * (ai->urx - ai->width));
      }
      else
      {
        voutln2("(CHARWD R %d)", ai->width);
        if (0 != (ht = texheight(ai)))
          voutln2("(CHARHT R %d)", ht);
        if (ai->lly)
          voutln2("(CHARDP R %d)", -ai->lly);
        if (ai->urx > ai->width)
          voutln2("(CHARIC R %d)", ai->urx - ai->width);
      }
 
      if (ai->adobenum != i || uppercase[i])
      {
        vleft();
        voutln("MAP");
        if (uppercase[i])
          voutln("(SELECTFONT D 1)");

        if (ai->pccs && ai->adobenum < 0)
        {
          xoff = 0;
          yoff = 0;

          for (npcc = ai->pccs; npcc; npcc = npcc->next)
            if (0 != (api = findadobe(npcc->partname)))
              if (api->texnum>=0)
              {
                if (npcc->xoffset != xoff)
                {
                  if (uppercase[i])
                  {
                    voutln2("(MOVERIGHT R %.1f)",
                            capheight * (npcc->xoffset - xoff));
                  }
                  else
                    voutln2("(MOVERIGHT R %d)", npcc->xoffset - xoff);

                  xoff = npcc->xoffset;
                }

                if (npcc->yoffset != yoff)
                {
                  if (uppercase[i])
                  {
                    voutln2("(MOVEUP R %.1f)",
                            capheight * (npcc->yoffset - yoff));
                  }
                  else
                    voutln2("(MOVEUP R %d)", npcc->yoffset - yoff);

                  yoff = npcc->yoffset;
                }

                voutln2("(SETCHAR O %o)", (unsigned)api->adobenum);
                xoff += texptrs[api->texnum]->width;
              }
        }
        else
          voutln2("(SETCHAR O %o)", (unsigned)ai->adobenum);
          vright();
      }
      vright();
    }

    if (level)
      Panic("ERROR: I forgot to match the parentheses\n");
}


void
version(f)
  FILE *f;
{
  extern DllImport char *kpathsea_version_string;


  fputs("ttf2tfm(k) 0.1\n", f);
  fprintf(f, "%s\n", kpathsea_version_string);
  fputs("Copyright (C) 1997 Frederic Loyer.\n", f);
  fputs("There is NO warranty.  You may redistribute this software\n", f);
  fputs("under the terms of the GNU General Public License\n", f);
  fputs("and the Dvips copyright.\n", f);
  fputs("For more information about these matters, see the files\n", f);
  fputs("named COPYING and ttf2tfm.c.\n", f);
  fputs("Primary author of ttf2tfm: F. Loyer.\n", f);
  fputs("ttf2tfm is based on afm2tfm from T. Rokicki\n", f);
  fputs("and the freetype project from\n", f);
  fputs("David Turner, Robert Wilhelm, and Werner Lemberg\n", f);
}


#define USAGE "\
  Convert a TrueType font table to TeX font metric format.\n\
\n\
-c REAL             use REAL for height of small caps made with -V [0.8]\n\
-e REAL             widen (extend) characters by a factor of REAL\n\
-O                  use octal for all character codes in the vpl file\n\
-p ENCFILE          read/download ENCFILE for the PostScript encoding\n\
-q                  suppress informational output\n\
-s REAL             oblique (slant) characters by REAL, generally <<1\n\
-t ENCFILE          read ENCFILE for the encoding of the vpl file\n\
-T ENCFILE          equivalent to -p ENCFILE -t ENCFILE\n\
-u                  output only characters from encodings, nothing extra\n\
-v FILE[.vpl]       make a VPL file for conversion to VF\n\
-V SCFILE[.vpl]     like -v, but synthesize smallcaps as lowercase\n\
--help              print this message and exit.\n\
--version           print version number and exit.\n\
"


void
usage(f)
  FILE *f;
{
  extern DllImport char *kpse_bug_address;

   
  fputs("Usage: ttf2tfm FILE[.ttf] [OPTION]... [FILE[.tfm]]\n", f);
  fputs(USAGE, f);
  putc('\n', f);
  fputs(kpse_bug_address, f);
}


void
openfiles(argc, argv)
  int argc;
  char *argv[];
{
  register int lastext;
  register int i;
  int arginc;


  tfmout = (FILE *)NULL;

#if defined(MSDOS) || defined(OS2) || defined(ATARIST)
  /* Make VPL file identical to that created under Unix */
  (void)sprintf(titlebuf, "ttf2tfm %s", argv[1]);
#else
#ifdef VMCMS
  /* Make VPL file identical to that created under Unix */
  (void)sprintf(titlebuf, "ttf2tfm %s", argv[1]);
#else
  (void)sprintf(titlebuf, "%s %s", argv[0], argv[1]);
#endif
#endif

  (void)strcpy(inname, argv[1]);
  lastext = -1;
  for (i = 0; inname[i]; i++)
    if (inname[i] == '.')
      lastext = i;
    else if (inname[i] == '/' || inname[i] == ':')
      lastext = -1;

  if (lastext == -1)
    (void)strcat(inname, ".ttf");

  while (argc > 2 && *argv[2] == '-')
  {
    arginc = 2;
    i = argv[2][1];
    if (i == '/')
      i = argv[2][2] - 32; /* a ==> A for VMS */

    switch (i)
    {
    case 'V':
      makevpl++;
    case 'v':
      makevpl++;
      (void)strcpy(outname, argv[3]);
      lastext = -1;
      for (i = 0; outname[i]; i++)
        if (outname[i] == '.')
          lastext = i;
        else if (outname[i] == '/' || outname[i] == ':')
          lastext = -1;

      if (lastext == -1)
        (void)strcat(outname, ".vpl");

#ifndef VMCMS
#ifndef ATARIST
      if ((vplout = fopen(outname, WRITEBIN)) == NULL)
#else
      if ((vplout = fopen(outname, "w")) == NULL)
#endif
#else
      if ((vplout = fopen(outname, "w")) == NULL)
#endif
        Panic("ERROR: Can't open vpl output file\n");
        break;

    case 'e':
      if (sscanf(argv[3], "%f", &efactor) == 0 || efactor < 0.01)
        Panic("ERROR: Bad extension factor\n");
      efactorparam = argv[3];
      break;

    case 'c':
      if (sscanf(argv[3], "%f", &capheight) == 0 || capheight < 0.01)
        Panic("ERROR: Bad small caps height\n");
      break;

    case 's':
      if (sscanf(argv[3], "%f", &slant) == 0)
        Panic("ERROR: Bad slant parameter\n");
      slantparam = argv[3];
      break;

    case 'P':
    case 'p':
      inenname = argv[3];
      break;

    case 'T':
      inenname = outenname = argv[3];
      break;

    case 't':
      outenname = argv[3];
      break;

    case 'O':
      forceoctal = 1;
      arginc = 1;
      break;

    case 'u':
      pedantic = 1;
      arginc = 1;
      break;

    case 'q':
      quiet = 1;
      arginc = 1;
      break;

    default:
      (void)fprintf(stderr, "Unknown option %s %s will be ignored.\n",
                    argv[2], argv[3]);
    }

    for (i = 0; i < arginc; i++)
    {
      (void)sprintf(titlebuf + strlen(titlebuf), " %s", argv[2]);
      argv++;
      argc--;
    }
  }

  real_inname = kpse_find_file(inname, kpse_type1_format, 1);
  if (!real_inname)
    Panic("ERROR: Could not find %s\n", inname);

  /* afmin = kpse_open_file(inname, kpse_afm_format); */

  if (argc > 3 || (argc == 3 && *argv[2] == '-'))
    Panic("ERROR: Need at most two non-option arguments\n");

  if (argc == 2)
    (void)strcpy(outname, inname);
  else
    (void)strcpy(outname, argv[2]);

  lastext = -1;
  for (i = 0; outname[i]; i++)
    if (outname[i] == '.')
      lastext = i;
    else if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\')
      lastext = -1;

  if (argc == 2)
  {
    outname[lastext] = 0;
    lastext = -1;
  }
  if (lastext == -1)
  {
    lastext = strlen(outname);
    (void)strcat(outname, ".tfm");
  }
  if (tfmout == NULL && (tfmout = fopen(outname, WRITEBIN)) == NULL)
    Panic("ERROR: Can't open tfm output file\n");
  outname[lastext] = 0;

  /*
   *   Now we strip off any directory information, so we only use the
   *   base name in the vf file.  We accept any of /, :, or \ as directory
   *   delimiters, so none of these are available for use inside the
   *   base name; this shouldn't be a problem.
   */

  for (i = 0, lastext = 0; outname[i]; i++)
    if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\')
      lastext = i + 1;

  if (lastext)
    strcpy(outname, outname + lastext);
}



/* Now we continue with the processing of encoding files. */


/*
 *   Some routines to remove kerns that match certain patterns.
 */

struct kern *
rmkernmatch(k, s)
  struct kern *k;
  char *s;
{
  struct kern *nk;


  while (k && strcmp(k->succ, s) == 0)
    k = k->next;

  if (k)
  {
    for (nk = k; nk; nk = nk->next)
      while (nk->next && strcmp(nk->next->succ, s) == 0)
        nk->next = nk->next->next;
  }
  return k;
}


/*
 *   Recursive to one level.
 */

void
rmkern(s1, s2, ai)
  char *s1, *s2;
  struct adobeinfo *ai;
{
  if (ai == 0)
  {
    if (strcmp(s1, "*") == 0)
    {
      for (ai = adobechars; ai; ai = ai->next)
        rmkern(s1, s2, ai);
      return;
    }
    else
    {
      ai = findadobe(s1);
      if (ai == 0)
        return;
    }
  }

  if (strcmp(s2, "*") == 0)
    ai->kerns = 0; /* drop them on the floor */
  else
    ai->kerns = rmkernmatch(ai->kerns, s2);
}


/* Make the kerning for character S1 equivalent to that for S2.
   If either S1 or S2 do not exist, do nothing.
   If S1 already has kerning, do nothing.  */

void
addkern (s1, s2)
  char *s1, *s2;
{
  struct adobeinfo *ai1 = findadobe(s1);
  struct adobeinfo *ai2 = findadobe(s2);


  if (ai1 && ai2 && !ai1->kerns)
  {
    /* Put the new one at the head of the list, since order is immaterial. */
    struct adobeptr *ap =
      (struct adobeptr *)mymalloc((unsigned long)sizeof (struct adobeptr));


    ap->next = ai2->kern_equivs;
    ap->ch = ai1;
    ai2->kern_equivs = ap;
  }
}


int sawligkern;

/*
 *   Reads a ligkern line, if this is one.  Assumes the first character
 *   passed is `%'.
 */

void
checkligkern(s)
  char *s;
{
  char *oparam = param;
  char *mlist[5];
  int n;


  s++;
  while (*s && *s <= ' ')
    s++;
  if (strncmp(s, "LIGKERN", 7) == 0)
  {
    sawligkern = 1;
    s += 7;
    while (*s && *s <= ' ')
      s++;
    param = s;
    while (*param)
    {
      for (n = 0; n < 5;)
      {
        if (*param == 0)
          break;
        mlist[n] = paramstring();
        if (strcmp(mlist[n], ";") == 0)
          break;
        n++;
      }

      if (n > 4)
        Panic("ERROR: Too many parameters in lig kern data\n");
      if (n < 3)
        Panic("ERROR: Too few parameters in lig kern data\n");

      if (n == 3 && strcmp(mlist[1], "{}") == 0)        /* rmkern command */
        rmkern(mlist[0], mlist[2], (struct adobeinfo *)0);
      else if (n == 3 && strcmp(mlist[1], "<>") == 0)   /* addkern */
        addkern(mlist[0], mlist[2]);
      else if (n == 3 && strcmp(mlist[0], "||") == 0 &&
               strcmp(mlist[1], "=") == 0)              /* bc command */
      {
        struct adobeinfo *ai = findadobe("||");


        if (boundarychar != -1)
          Panic("ERROR: Multiple boundary character commands?\n");
        if (sscanf(mlist[2], "%d", &n) != 1)
          Panic("ERROR: Expected number assignment for boundary char\n");
        if (n < 0 || n > 255)
          Panic("ERROR: Boundary character number must be 0..255\n");

        boundarychar = n;
        if (ai == 0)
          Panic("ERROR: Internal error: boundary char\n");
        ai->texnum = n; /* prime the pump, so to speak, for lig/kerns */
      }
      else if (n == 4)
      {
        int op = -1;
        struct adobeinfo *ai;


        for (n = 0; encligops[n]; n++)
          if (strcmp(mlist[2], encligops[n]) == 0)
          {
            op = n;
            break;
          }
        if (op < 0)
          Panic("ERROR: Bad ligature op specified\n");

        if (0 != (ai = findadobe(mlist[0])))
        {
          struct lig *lig;


          if (findadobe(mlist[2]))     /* remove coincident kerns */
            rmkern(mlist[0], mlist[1], ai);
          if (strcmp(mlist[3], "||") == 0)
            Panic("ERROR: You can't lig to the boundary character!\n");
          if (!fixedpitch)              /* fixed pitch fonts get *0* ligs */
          {
            for (lig = ai->ligs; lig; lig = lig->next)
              if (strcmp(lig->succ, mlist[1]) == 0)
                break;                  /* we'll re-use this structure */

            if (lig == 0)
            {
              lig = newlig();
              lig->succ = newstring(mlist[1]);
              lig->next = ai->ligs;
              ai->ligs = lig;
            }
            lig->sub = newstring(mlist[3]);
            lig->op = op;
 
            if (strcmp(mlist[1], "||") == 0)
            {
              lig->boundleft = 1;
              if (strcmp(mlist[0], "||") == 0)
                Panic("ERROR: You can't lig boundarychar boundarychar!\n");
            }
            else
              lig->boundleft = 0;
          }
        }
      }
      else
        Panic("ERROR: Bad form in LIGKERN command\n");
    }
  }
  param = oparam;
}



/*
 *   Here we get a token from the TTF file.  We parse just as much PostScript
 *   as we expect to find in an encoding file.  We allow commented lines and
 *   names like 0, .notdef, _foo_.  We do not allow //abc.
 */

char smbuffer[100];    /* for tokens */


char *
gettoken()
{
  char *p, *q;


  while (1)
  {
    while (param == 0 || *param == 0)
    {
      if (getline() == 0)
        Panic("ERROR: Premature end in encoding file\n");
      for (p = buffer; *p; p++)
        if (*p == '%')
        {
          if (ignoreligkern == 0)
            checkligkern(p);
          *p = 0;
          break;
        }
    }

    while (*param && *param <= ' ')
      param++;

    if (*param)
    {
      if (*param == '[' || *param == ']' ||
          *param == '{' || *param == '}')
      {
        smbuffer[0] = *param++;
        smbuffer[1] = 0;
        return smbuffer;
      }
      else if (*param == '/' || *param == '-' || *param == '_' ||
               *param == '.' ||
               ('0' <= *param && *param <= '9') ||
               ('a' <= *param && *param <= 'z') ||
               ('A' <= *param && *param <= 'Z'))
      {
        smbuffer[0] = *param;
        for (p = param+1, q = smbuffer+1;
             *p == '-' || *p == '_' || *p == '.' ||
             ('0' <= *p && *p <= '9') ||
             ('a' <= *p && *p <= 'z') ||
             ('A' <= *p && *p <= 'Z'); p++, q++)
          *q = *p;

        *q = 0;
        param = p;
        return smbuffer;
      }
    }
  }
}


void
getligkerndefaults()
{
  int i;


  for (i = 0; staticligkern[i]; i++)
  {
    strcpy(buffer, staticligkern[i]);
    strcpy(obuffer, staticligkern[i]);
    param = buffer;
    checkligkern(buffer);
  }
}


/*
 *   This routine reads in an encoding file, given the name.  It returns
 *   the final total structure.  It performs a number of consistency checks.
 */

struct encoding *
readencoding(enc)
  char *enc;
{
  char *real_enname;
  char *p;
  int i;
  struct encoding *e =
    (struct encoding *)mymalloc((unsigned long)sizeof (struct encoding));


  sawligkern = 0;

  if (afmin)
    Panic("ERROR: Oops; internal afmin error\n");

  if (enc)
  {
    real_enname = kpse_find_file(enc, kpse_tex_ps_header_format, 1);
    if (!real_enname)
      Panic("ERROR: Couldn't find encoding file `%s'\n", enc);

    afmin = fopen(real_enname, "r");
    if (afmin == 0)
      Panic("ERROR: Couldn't open encoding file `%s'\n", enc);

    param = 0;
    p = gettoken();
    if (*p != '/' || p[1] == 0)
      Panic("ERROR: First token in encoding must be literal encoding name\n");
    e->name = newstring(p+1);
    p = gettoken();
    if (strcmp(p, "["))
      Panic("ERROR: Second token in encoding must be mark ([) token\n");

    for (i = 0; i < 256; i++)
    {
      p = gettoken();
      if (*p != '/' || p[1] == 0)
        Panic("ERROR: Tokens 3 to 257 in encoding must be literal names\n");
      e->vec[i] = newstring(p+1);
    }

    p = gettoken();
    if (strcmp(p, "]"))
      Panic("ERROR: Token 258 in encoding must be make-array (])\n");

    while (getline())
    {
      for (p = buffer; *p; p++)
        if (*p == '%')
        {
          if (ignoreligkern == 0)
            checkligkern(p);
          *p = 0;
          break;
        }
    }

    fclose(afmin);
    afmin = 0;
    if (ignoreligkern == 0 && sawligkern == 0)
      getligkerndefaults();
  }
  else
  {
    e = &staticencoding;
    getligkerndefaults();
  }
  param = 0;
  return e;
}


/*
 *   This routine prints out the line that needs to be added to ttfonts.map.
 */

void
consttfonts()
{
  (void)printf("\n");
  (void)printf("%s %s", outname, inname);

  if (slantparam || efactorparam || inenname)
  {
    if (slantparam)
      (void)printf(" Slant=%s", slantparam);
    if (efactorparam)
      (void)printf(" Extend=%s", efactorparam);
    if (inenname)
      (void)printf(" Encoding=%s", inenname);
  }
  (void)printf("\n");
}



int
main(argc, argv)
  int argc;
  char *argv[];
{
  int i;


  kpse_set_progname(argv[0]);

  if (argc == 1)
  {
    fputs("ttf2tfm: Need at least one file argument.\n", stderr);
    fputs("Try `ttf2tfm --help' for more information.\n", stderr);
    exit(1);
  }     
  if (argc == 2)
  {
    if (strcmp(argv[1], "--help") == 0)
    {
      usage(stdout);
      exit(0);
    }
    else if (strcmp(argv[1], "--version") == 0)
    {
      version(stdout);
      exit(0);
    }
  }

  for (i = 0; i < 256; i++)
    nexttex[i] = -1; /* encoding chains have length 0 */
  tfmdata = (long *)mymalloc((unsigned long)40000L);

  openfiles(argc, argv);

  readttf();

  if (fontspace == 0)
  {
    struct adobeinfo *ai;


    if (0 != (ai = findadobe("space")))
      fontspace = ai->width;
    else if (adobeptrs[32])
      fontspace = adobeptrs[32]->width;
    else
      fontspace = transform(500, 0);
  }

  handlereencoding();
  buildtfm();
  writetfm();
  consttfonts();

  if (makevpl)
  {
    assignchars();
    if (makevpl > 1)
      upmap();
    writevpl();
  }

  return 0;
}


/* end */
