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

#include <stdio.h>
#include <string.h>
#include <math.h>       /* libc ANSI */
#include <ctype.h>


#ifdef __GNUC__
#define NORETURN        volatile
#else
#define NORETURN        /* nothing */
#endif

#include <kpathsea/kpathsea.h>

#include "enclib.h"
#include "pklib.h"
#include "ttflib.h"


char ident[] = "ttf2pk version 1.0";

typedef char Boolean;

#define True    1
#define False   0

int dpi;
int ptsize;

#define BUF_SIZE 255

char buffer[BUF_SIZE];  /* input buffer (modified while parsing) */
char obuffer[BUF_SIZE];
char *param;

FILE *afmin;            /* The .enc file */
FILE *config_file;      /* The ttfonts.map file */

char quiet;


/*
 *      Print error message and quit.
 */

static NORETURN void
oops(const char *message, ...)
{
  va_list args;


  va_start(args, message);
  vfprintf(stderr, message, args);
  va_end(args);
  putc('\n', stderr);

  exit(1);
}


/*
 *      Either allocate storage or fail with explanation.
 */

char *
mymalloc(size, why)
  unsigned size;
  const char *why;
{
  char *mem = (char *)malloc(size);


  if (mem == NULL)
    oops("Cannot allocate %u bytes for %s.", size, why);
  return mem;
}


/*
 *      Either reallocate storage or fail with explanation.
 */

char *
myrealloc(oldp, size, why)
  char *oldp;
  unsigned size;
  const char *why;
{
  char *mem;


  mem = oldp == NULL ? (char *)malloc(size)
                     : (char *)realloc(oldp, size);
  if (mem == NULL)
    oops("Cannot reallocate %u bytes for %s.", size, why);
  return mem;
}


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", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
    "", "", "", "", "", "", "" 
   }
};


/*
 * Here is the encoding parser taken from afm2tfm
 */
void
print_error(s)
  register char *s;
{
  (void)fprintf(stderr, "%s\n", s);

  if (obuffer[0])
  {
    (void)fprintf(stderr, "%s\n", obuffer);
    while (param > buffer)
    {
      (void)fprintf(stderr, " ");
      param-- ;
    }
    (void)fprintf(stderr, "^\n");
  }

  exit(1);
}


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


/* 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;
}


/*
 *   Here we get a token from the AFM 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)
        print_error("ERROR: Premature end in encoding file");
      for (p = buffer; *p; p++)
        if (*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;
      }
    }
  }
}


/*
 *   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),
                                "reading encoding");


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

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

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

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

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

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

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

    fclose(afmin);
    afmin = 0;
  }
  else
    e = &staticencoding;

  param = 0;
  return e;
}


int 
main(int argc, char** argv)
{
  /* char* filename;
     int platform;
     int ttf_encoding; */
  int i;
  int charlist[257], *charlist_ptr;
  char *configline, *p, *q, endc;
  unsigned int cflinelen;
  struct encoding *enc;
  char *fontname;
  int fontname_len;
  char *ttf_filename, *tfm_filename, *pk_filename, *enc_filename;
  char *real_ttf_filename;
  int slant, extend;


  kpse_set_progname(argv[0]);
  kpse_init_prog("TTF2PK", 300, "cx", "cmr10");

  xputenv_int("KPATHSEA_DPI", 300);

  if (argc > 1 && argv[1][0] == '-')
  {
    if (argv[1][1] == 'q')
      quiet = 1;
    else
      fprintf(stderr, "Unknown option `%s' will be ignored\n", argv[1]);

    argv++;
    argc--;
  }

  if (argc != 3 || (dpi = atoi(argv[2])) <= 0)
  {
    fputs("Usage: ttf2pk [-q] <font> <dpi>\n", stderr);
    exit(1);
  }

  fontname = argv[1];
  fontname_len = strlen(fontname);
  enc_filename = NULL;
  ptsize = 10;

  slant = 0;
  extend = 1024;
  config_file = fopen(kpse_find_file("ttfonts.map",
                                     kpse_dvips_config_format, 1), "r");
  
  if (config_file == NULL) 
    oops("ERROR: Cannot find file ttfonts.map");

  configline = (char *)mymalloc(cflinelen = 80, "Config file line");

  do
  {
    int len = 0;

    
    if (fgets(configline, cflinelen, config_file) == NULL)
      oops("ERROR: Cannot find font %s in config file", fontname);

    for (;;)
    {
      i = strlen(configline + len);
      
      len += i;
      if (len > 0 && configline[len - 1] == '\n')
      {
        configline[--len] = '\0';
        break;
      }
      if (len < cflinelen - 1)
        break;
      configline = myrealloc(configline, cflinelen += 80,
                             "config file line");
      fgets(configline + len, cflinelen - len, config_file);
    }
  } while (memcmp(configline, fontname, fontname_len) != 0 ||
           (configline[fontname_len] != '\0' &&
            !isspace(configline[fontname_len])));

  fclose(config_file);

  /*
   * Parse the line from the config file.
   */

  for (p = configline + fontname_len; *p != '\0'; ++p)
  {
    if (!isspace(*p))
      break;
  }

  ttf_filename = p;

  while (*p != '\0' && !isspace(*p))
    ++p;
  *p++= 0;      

  for (; *p != '\0'; ++p)
  {
    if (isspace(*p))
      continue;
    q = p;

    while (*p != '\0' && !isspace(*p))
      ++p;
    endc = *p;
    *p = 0;

    if (!strncmp(q, "Slant=", 6))
    {
      float s;


      q += 6;
      if (sscanf(q, "%f", &s) == 0)
        print_error("ERROR: Bad slant parameter");
      slant = floor(s * 1024);
    }
    else if (!strncmp(q, "Encoding=", 9))
    {
      q += 9;
      enc_filename = q;
    }
    else if (!strncmp(q, "Extend=", 7))
    {
      float e;


      q += 7;
      if (sscanf(q, "%f", &e) == 0)
        print_error("ERROR: Bad Extend parameter");
      extend = floor(e * 1024);
    }
  }

  tfm_filename = mymalloc(fontname_len + 5);
  sprintf(tfm_filename, "%s.tfm", fontname);
  
  pk_filename = mymalloc(fontname_len + 10);
  sprintf(pk_filename, "%s.%dpk", fontname, dpi);

  enc = readencoding(enc_filename);
  TFMopen(tfm_filename, charlist);
  PKopen(pk_filename, fontname);
  real_ttf_filename = kpse_find_file(ttf_filename, kpse_type1_format, 1);
  if (!real_ttf_filename)
    oops("ERROR: Could not find %s\n", ttf_filename);
  TTFopen(real_ttf_filename, ptsize, dpi, extend, slant);

  for (charlist_ptr = charlist; *charlist_ptr >= 0; charlist_ptr++)
  {
    char *name;
    int code;


    name = enc->vec[*charlist_ptr];
    code = adobename_to_code(enc->vec[*charlist_ptr]);
    if (code < 0)
      fprintf(stderr, "Unknown char: %s\n", name);
    else
    {
      byte *bitmap;
      int w, h, hoff, voff;


      if (!quiet)
      {
        printf("Processing %3d %04x %s", *charlist_ptr, code, name);
        fflush(stdout);
      }

      TTFprocess(code, &bitmap, &w, &h, &hoff, &voff);

      if (!quiet)
        printf("(%d,%d)\n", hoff, voff);

      PKputglyph(*charlist_ptr,
                 -hoff, -voff, w - hoff, h - voff,
                 w, h, bitmap);
    }
  }

  PKclose();
  exit(0);
}


/* end */
