/*
 *  Authors:
 *    Raph Levien (raph@acm.org)
 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include <glib.h>

#include <libgnomeprint/gp-ps-unicode.h>

#include <libart_lgpl/art_misc.h>
#include <libart_lgpl/art_rect.h>
#include <libart_lgpl/art_vpath.h>
#include <libart_lgpl/art_bpath.h>
#include <libart_lgpl/art_vpath_bpath.h>
#include <libart_lgpl/art_vpath_dash.h>
#include <libart_lgpl/art_svp.h>
#include <libart_lgpl/art_svp_vpath.h>
#include <libart_lgpl/art_svp_wind.h>
#include <libart_lgpl/art_svp_point.h>

#include "gt1-misc.h"
#include "gt1-region.h"
#include "gt1-namecontext.h"
#include "gt1-value.h"
#include "gt1-dict.h"

#include "gt1-parset1.h"
#include "parseAFM.h"


/* a big-assed module to parse Adobe Type 1 fonts into meaningful
   info */

#define noVERBOSE

static int
read_int32_lsb (const char *p)
{
  const unsigned char *q = (unsigned char *)p;

  return q[0] + (q[1] << 8) + (q[2] << 16) + (q[3] << 24);
}

/* this is a pfb to pfa converter

 Reference: Adobe technical note 5040, "Supporting Downloadable PostScript
 Language Fonts", page 9 */
static char *
pfb_to_flat (const char *input, int input_size)
{
  const unsigned char *in = (unsigned char *)input;
  char *flat;
  int flat_size, flat_size_max;
  int in_idx;
  int length;
  int i;
  const char hextab[16] = "0123456789abcdef";

  flat_size = 0;
  flat_size_max = 32768;
  flat = gt1_new (char, flat_size_max);

  for (in_idx = 0; in_idx < input_size;)
    {
      if (in[in_idx] != 128)
	{
	  gt1_free (flat);
	  return NULL;
	}
      switch (in[in_idx + 1])
	{
	case 1:
	  length = read_int32_lsb (input + in_idx + 2);
	  if (flat_size + length > flat_size_max)
	    {
	      do
		flat_size_max <<= 1;
	      while (flat_size + length > flat_size_max);
	      flat = gt1_renew (flat, char, flat_size_max);
	    }
	  in_idx += 6;
	  memcpy (flat + flat_size, in + in_idx, length);
	  flat_size += length;
	  in_idx += length;
	  break;
	case 2:
	  length = read_int32_lsb (input + in_idx + 2);
	  if (flat_size + length * 3 > flat_size_max)
	    {
	      do
		flat_size_max <<= 1;
	      while (flat_size + length * 3 > flat_size_max);
	      flat = gt1_renew (flat, char, flat_size_max);
	    }
	  in_idx += 6;
	  for (i = 0; i < length; i++)
	    {
	      flat[flat_size++] = hextab[in[in_idx] >> 4];
	      flat[flat_size++] = hextab[in[in_idx] & 15];
	      in_idx++;
	      if ((i & 31) == 31 || i == length - 1)
		flat[flat_size++] = '\n';
	    }
	  break;
	case 3:
	  /* zero terminate the returned string */
	  if (flat_size == flat_size_max)
	    gt1_double (flat, char, flat_size_max);
	  flat[flat_size] = 0;
	  return flat;
	default:
	    gt1_free (flat);
	    return NULL;
	}
    }
  return flat;
}

struct _Gt1TokenContext {
  char *source;
  int index;
  int pos;
};

typedef enum {
  TOK_NUM,
  TOK_STR,
  TOK_NAME, /* initial / */
  TOK_IDENT,
  TOK_OPENBRACE,
  TOK_CLOSEBRACE,
  TOK_END
} TokenType;

/* we're phasing this out in favor of value.h's Gt1String */
typedef struct _MyGt1String MyGt1String;

struct _MyGt1String {
  char *start;
  char *fin;
};

static void
tokenize_free (Gt1TokenContext *tc)
{
  gt1_free (tc->source);
  gt1_free (tc);
}


static Gt1TokenContext *
tokenize_new (const char *input)
{
  Gt1TokenContext *tc;
  int length;

  tc = gt1_new (Gt1TokenContext, 1);
  length = strlen (input);
  tc->source = gt1_new (char, length + 1);
  memcpy (tc->source, input, length + 1);
  tc->index = 0;
  tc->pos = 0;

  return tc;
}

static Gt1TokenContext *
tokenize_new_from_mystring (MyGt1String *input)
{
  Gt1TokenContext *tc;
  int length;

  tc = gt1_new (Gt1TokenContext, 1);
  length = input->fin - input->start;
  tc->source = gt1_new (char, length + 1);
  memcpy (tc->source, input->start, length + 1);
  tc->index = 0;
  tc->pos = 0;

  return tc;
}

/* this returns a TokenType, and sets result to the token contents.

   Note: this strips delimiters, like the initial /, and the enclosing ().
*/
static TokenType
tokenize_get (Gt1TokenContext *tc, MyGt1String *result)
{
  unsigned char *s = (unsigned char *)tc->source;
  int index = tc->index;
  int pos = tc->pos;
  unsigned char c;
  TokenType type;

  /* skip comments altogether (maybe later we want them, though) */
  while (c = s[index], isspace (c) || c == '%')
    {
      /* skip leading whitespace */
      while (isspace (s[index]))
	{
	  if (s[index] == '\r' || s[index] == '\n')
	    pos = 0;
	  else
	    pos++;
	  index++;
	}

      if (s[index] == '%')
	{
	  do
	    /* skip past end-of-line */
	    {
	      while (c = s[index], c && c != '\r' && c != '\n')
		index++;
	      if (s[index] != 0) index++;
	    }
	  while (s[index] == '%');
	}
    }

  /* skip leading whitespace */
  while (c = s[index], isspace (c))
    {
      if (c == '\r' || c == '\n')
	pos = 0;
      else
	pos++;
      index++;
    }


  /* ok, so now we're at the actual start of a token */
  result->start = s + index;

  c = s[index];

  if (c == 0)
    {
      result->fin = s + index;
      type = TOK_END;
    }
  /* note: GhostScript checks much more strenuously. Further, this
     predicate does not pass the valid number -.9 */
  else if (isdigit (c) || c == '.' || (c == '-' && isdigit (s[index + 1])))
    {
      /* numeric token */
      while (c = s[index], c && !isspace (c) && c != '{' && c != '/' &&
	     c != '[' && c != ']' && c != '}')
	{
	  index++;
	  pos++;
	}
      result->fin = s + index;
      type = TOK_NUM;
    }
  else if (c == '/')
    {
      /* an atom, or whatever that's called */
      index++;
      result->start = s + index;
      while (c = s[index], c && !isspace (c) && c != '{' && c != '/' &&
	     c != '[' && c != ']' && c != '}' && c != '(')
	{
	  index++;
	  pos++;
	}
      result->fin = s + index;
      type = TOK_NAME;
    }
  else if (c == '(')
    {
      int nest;
      int backslash;

      nest = 1;
      index++;
      backslash = 0;
      result->start = s + index;
      while (c = s[index], c && nest)
	{
	  if (backslash)
	    backslash = 0;
	  else if (c == '(')
	    nest++;
	  else if (c == ')')
	    nest--;
	  else if (c == '\\')
	    backslash = 1;
	  index++;
	  if (c == '\r' || c == '\n')
	    pos = 0;
	  else
	    pos++;
	}
      /* we could have a c == 0 error case here */
      result->fin = s + index - 1;
      type = TOK_STR;
    }
  else if (c == '{')
    {
      index++;
      result->fin = s + index;
      type = TOK_OPENBRACE;
    }
  else if (c == '}')
    {
      index++;
      result->fin = s + index;
      type = TOK_CLOSEBRACE;
    }
  else if (c == '[' || c == ']')
    {
      index++;
      result->fin = s + index;
      type = TOK_IDENT;
    }
  else
    {
      /* treat everything else as an identifier */
      while (c = s[index], c && !isspace (c) && c != '{' && c != '/' &&
	     c != '[' && c != ']' && c != '}' && c != '(')
	{
	  index++;
	  pos++;
	}
      result->fin = s + index;
      if (isspace(c))
	index++; /* skip single trailing whitespace char - this is
		    useful for readstring */
      type = TOK_IDENT;
    }

  tc->index = index;
  tc->pos = pos;
  return type;
}

static int
ascii_to_hex (unsigned char c)
{
  if (c <= '9') return c - '0';
  else if (c >= 'a') return c + 10 - 'a';
  else return c + 10 - 'A';
}

/* return a hex byte, or -1 on error */

/* we don't deal with comments here */
static int
tokenize_get_hex_byte (Gt1TokenContext *tc)
{
  const unsigned char *s = (const unsigned char *)tc->source;
  int index = tc->index;
  int pos = tc->pos;
  int byte;

  /* skip leading whitespace */
  while (isspace (s[index]))
    {
      if (s[index] == '\r' || s[index] == '\n')
	pos = 0;
      else
	pos++;
      index++;
    }

  if (isxdigit (s[index]) && isxdigit (s[index + 1]))
    {
      byte = (ascii_to_hex (s[index]) << 4) | ascii_to_hex (s[index + 1]);
      index += 2;
    }
  else
    byte = -1;

  tc->index = index;
  tc->pos = pos;
  return byte;
}

/* careful, we're _not_ protected against buffer overruns here */
/* todo: fix this, it's definitely a potential security violation.
   This almost certainly implies changing the Gt1TokenContext structure
   to incorporate a size field, and de-emphasizing the use of
   zero-termination */
static void
tokenize_get_raw (Gt1TokenContext *tc, char *buf, int buf_size)
{
  memcpy (buf, tc->source + tc->index, buf_size);
  tc->index += buf_size;
}

#ifdef DEBUG

static void
print_token (TokenType type, MyGt1String *lexeme)
{
  char *start, *fin;

  start = lexeme->start;
  fin = lexeme->fin;

  switch (type)
    {
    case TOK_NUM:
      printf ("number        ");
      break;
    case TOK_IDENT:
      printf ("identifier    ");
      break;
    case TOK_NAME:
      printf ("name          ");
      break;
    case TOK_STR:
      printf ("string        ");
      break;
    case TOK_OPENBRACE:
      printf ("open brace    ");
      break;
    case TOK_CLOSEBRACE:
      printf ("close brace   ");
      break;
    case TOK_END:
      printf ("end           ");
      break;
    default:
      break;
    }
  while (start != fin)
    printf ("%c", *start++);
  printf ("\n");
}

static void
test_token (const char *flat)
{
  Gt1TokenContext *tc;
  TokenType type;
  MyGt1String lexeme;

  tc = tokenize_new (flat);
  while (1)
    {
      type = tokenize_get (tc, &lexeme);
      if (type == TOK_END) break;
      print_token (type, &lexeme);
     }
}

#endif

/* basic PostScript language types */

typedef struct _Gt1ProcStep {
  TokenType tok_type;
  MyGt1String lexeme;
} Gt1ProcStep;

struct _Gt1Proc {
  int n_steps;
  /* sooner or later, we'll want to replace proc steps with
     plain PostScript values. - probably sooner in fact, because that's
     the best way to implement nested procedures, which currently do
     not work. */
  Gt1Value steps[1];
};

/* low-level PostScript routines */

/* note: resulting array is _uninitialized_ ! */
static Gt1Array *
array_new (Gt1Region *r, int size)
{
  Gt1Array *array;

  array = (Gt1Array *)gt1_region_alloc (r, sizeof(Gt1Array) +
				 (size - 1) * sizeof(Gt1Value));
  array->n_values = size;
  return array;
}

struct _Gt1PSContext {
  Gt1Region *r; /* the region all PS values are allocated into */

  Gt1TokenContext *tc; /* this is for readstring, eexec, etc. */

  Gt1NameContext *nc; /* the context for all names */

  Gt1Value *value_stack;
  int n_values, n_values_max;

  /* ghostscript also has an execution stack - what's that? */

  Gt1Dict **gt1_dict_stack;
  int n_dicts, n_dicts_max;

  /* a special dict that holds all the fonts */
  Gt1Dict *fonts;

  Gt1TokenContext **file_stack; /* invariant: top of file stack == tc */
  int n_files, n_files_max;

  int quit; /* maybe this should be a string, for error messages too */
};

/* a very basic PostScript interpreter */

/* make sure that value stack has enough room for pushing n values. */
static void
ensure_stack (Gt1PSContext *psc, int n)
{
  if (psc->n_values + n == psc->n_values_max)
    {
      psc->n_values_max <<= 1;
      psc->value_stack = gt1_renew (psc->value_stack, Gt1Value, psc->n_values_max);
    }
}

static double
parse_num (MyGt1String *number)
{
  double sign;
  double mantissa;
  double decimal;
  int exp_sign;
  int exp;
  int i, length;
  const unsigned char *start;

  start = (const unsigned char *)number->start;
  length = number->fin - number->start;

  i = 0;
  sign = 1;
  if (i < length && start[i] == '-')
    {
      sign = -1;
      i++;
    }
  else if (i < length && start[i] == '+')
    i++;

  mantissa = 0;
  while (i < length && isdigit (start[i]))
    {
      mantissa = (mantissa * 10) + start[i] - '0';
      i++;
    }
  if (i < length && start[i] == '.')
    {
      i++;
      decimal = 1;
      while (i < length && isdigit (start[i]))
	{
	  decimal *= 0.1;
	  mantissa += (start[i] - '0') * decimal;
	  i++;
	}
    }
  if (i < length && (start[i] == 'e' || start[i] == 'E'))
    {
      i++;
      exp_sign = 1;
      if (i < length && start[i] == '-')
	{
	  exp_sign = -1;
	  i++;
	}
      else if (i < length && start[i] == '+')
	i++;

      exp = 0;
      while (i < length && isdigit (start[i]))
	{
	  exp = (exp * 10) + start[i] - '0';
	  i++;
	}
      mantissa *= pow (10, exp * exp_sign);
    }
  return sign * mantissa;
}

#ifdef DEBUG

static void
print_mystring (MyGt1String *str)
{
  char *start, *fin;

  start = str->start;
  fin = str->fin;

  while (start != fin)
    printf ("%c", *start++);
}

#endif

static void
print_string (Gt1String *str)
{
  char *start;
  int size;
  int i;

  start = str->start;
  size = str->size;

  for (i = 0; i < size; i++);
    printf ("%c", start[i]);
}

static void
print_value (Gt1PSContext *psc, Gt1Value *val)
{
  switch (val->type)
    {
    case GT1_VAL_NUM:
      printf ("%g", val->val.num_val);
      break;
    case GT1_VAL_BOOL:
      printf ("%s", val->val.bool_val ? "true" : "false");
      break;
    case GT1_VAL_STR:
      printf ("\"");
      print_string (&val->val.str_val);
      printf ("\"");
      break;
    case GT1_VAL_NAME:
      printf ("/%s", gt1_name_context_string (psc->nc, val->val.name_val));
      break;
    case GT1_VAL_UNQ_NAME:
      printf ("%s", gt1_name_context_string (psc->nc, val->val.name_val));
      break;
    case GT1_VAL_DICT:
      printf ("<dictionary %d/%d>",
	      val->val.dict_val->n_entries,
	      val->val.dict_val->n_entries_max);
      break;
    case GT1_VAL_ARRAY:
      printf ("<array>");
      break;
    case GT1_VAL_PROC:
#if 1
      printf ("<proc>");
#else
      printf ("{ ");
      { int i;
      for (i = 0; i < val->val.proc_val->n_values; i++)
	{
	  print_value (psc, &val->val.proc_val->vals[i]);
	  printf (" ");
	}
      printf ("}");
      }
#endif
      break;
    case GT1_VAL_FILE:
      printf ("<file>");
      break;
    case GT1_VAL_INTERNAL:
      printf ("<internal function>");
    case GT1_VAL_MARK:
      printf ("<mark>");
      break;
    default:
      printf ("???%d", val->type);
    }
}

#ifdef DEBUG

static void
print_token_short (TokenType type, MyGt1String *lexeme)
{
  char *start, *fin;

  start = lexeme->start;
  fin = lexeme->fin;

  switch (type)
    {
    case TOK_NUM:
      print_mystring (lexeme);
      break;
    case TOK_IDENT:
      print_mystring (lexeme);
      break;
    case TOK_NAME:
      printf ("/");
      print_mystring (lexeme);
      break;
    case TOK_STR:
      printf ("(");
      print_mystring (lexeme);
      printf (")");
      break;
    case TOK_OPENBRACE:
      printf ("{");
      break;
    case TOK_CLOSEBRACE:
      printf ("}");
      break;
    case TOK_END:
      printf ("end           ");
      break;
    default:
      break;
    }
}

#endif

static void
print_value_deep (Gt1PSContext *psc, Gt1Value *val, int nest)
{
  int i, j;

  for (i = 0; i < nest; i++)
    printf (" ");

  switch (val->type)
    {
    case GT1_VAL_NUM:
      printf ("%g", val->val.num_val);
      break;
    case GT1_VAL_BOOL:
      printf ("%s", val->val.bool_val ? "true" : "false");
      break;
    case GT1_VAL_STR:
      printf ("\"");
      print_string (&val->val.str_val);
      printf ("\"");
      break;
    case GT1_VAL_NAME:
      printf ("/%s", gt1_name_context_string (psc->nc, val->val.name_val));
      break;
    case GT1_VAL_UNQ_NAME:
      printf ("%s", gt1_name_context_string (psc->nc, val->val.name_val));
      break;
    case GT1_VAL_DICT:
      printf ("<dictionary %d/%d> [\n",
	      val->val.dict_val->n_entries,
	      val->val.dict_val->n_entries_max);
      for (i = 0; i < val->val.dict_val->n_entries; i++)
	{
	  for (j = 0; j < nest; j++)
	    printf (" ");
	  printf ("key %d\n", val->val.dict_val->entries[i].key);
	  print_value_deep (psc, &val->val.dict_val->entries[i].val, nest + 1);
	}
      for (j = 0; j < nest; j++)
	printf (" ");
      printf ("]");
      break;
    case GT1_VAL_ARRAY:
      printf ("[\n");
      for (i = 0; i < val->val.array_val->n_values; i++)
	{
	  print_value_deep (psc, &val->val.array_val->vals[i], nest + 1);
	}
      for (j = 0; j < nest; j++)
	printf (" ");
      printf ("]");
      break;
    case GT1_VAL_PROC:
      printf ("{\n");
      for (i = 0; i < val->val.proc_val->n_values; i++)
	{
	  print_value_deep (psc, &val->val.proc_val->vals[i], nest + 1);
	}
      for (j = 0; j < nest; j++)
	printf (" ");
      printf ("}");
      break;
    case GT1_VAL_FILE:
      printf ("<file>");
      break;
    case GT1_VAL_INTERNAL:
      printf ("<internal function>");
    case GT1_VAL_MARK:
      printf ("<mark>");
      break;
    default:
      printf ("???");
    }
  printf ("\n");
}

#ifdef DEBUG

static void
print_stack (Gt1PSContext *psc)
{
  int i;

  for (i = 0; i < psc->n_values; i++)
    {
      print_value (psc, &psc->value_stack[i]);
      if (i != psc->n_values - 1)
	printf (" ");
    }
  printf ("\n");
}
#endif

static void
eval_ps_val (Gt1PSContext *psc, Gt1Value *val);

static void
eval_proc (Gt1PSContext *psc, Gt1Proc *proc)
{
  int i;

#ifdef VERBOSE
  printf ("begin proc evaluation\n");
#endif
  for (i = 0; !psc->quit && i < proc->n_values; i++)
    eval_ps_val (psc, &proc->vals[i]);
#ifdef VERBOSE
  printf ("end proc evaluation\n");
#endif
}

static Gt1Value *
gt1_dict_stack_lookup (Gt1PSContext *psc, Gt1NameId key)
{
  int i;
  Gt1Value *val;

  for (i = psc->n_dicts - 1; i >= 0; i--)
    {
      val = gt1_dict_lookup (psc->gt1_dict_stack[i], key);
      if (val != NULL)
	return val;
    }
  return NULL;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_number (Gt1PSContext *psc, double *result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_NUM)
    {
      printf ("type error - expecting number\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.num_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_dict (Gt1PSContext *psc, Gt1Dict **result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_DICT)
    {
      printf ("type error - expecting dict\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.dict_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_name (Gt1PSContext *psc, Gt1NameId *result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_NAME)
    {
      printf ("type error - expecting atom\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.name_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_file (Gt1PSContext *psc, Gt1TokenContext **result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_FILE)
    {
      printf ("type error - expecting file\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.file_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_string (Gt1PSContext *psc, Gt1String *result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_STR)
    {
      printf ("type error - expecting string\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.str_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_array (Gt1PSContext *psc, Gt1Array **result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_ARRAY)
    {
      printf ("type error - expecting array\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.array_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_bool (Gt1PSContext *psc, int *result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_BOOL)
    {
      printf ("type error - expecting bool\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.bool_val;
  return 1;
}

/* return 1 on success, with result set to the top of the value stack */
static int
get_stack_proc (Gt1PSContext *psc, Gt1Proc **result, int index)
{
  if (psc->n_values < index)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return 0;
    }
  if (psc->value_stack[psc->n_values - index].type != GT1_VAL_PROC)
    {
      printf ("type error - expecting proc\n");
      psc->quit = 1;
      return 0;
    }
  *result = psc->value_stack[psc->n_values - index].val.proc_val;
  return 1;
}

/* here begin the internal procedures */

static void
internal_dict (Gt1PSContext *psc)
{
  Gt1Dict *dict;
  double d_size;

  if (get_stack_number (psc, &d_size, 1))
    {
      dict = gt1_dict_new (psc->r, d_size);
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_DICT;
      psc->value_stack[psc->n_values - 1].val.dict_val = dict;
    }
}

static void
internal_begin (Gt1PSContext *psc)
{
  Gt1Dict *dict;

  if (get_stack_dict (psc, &dict, 1))
    {
      if (psc->n_dicts == psc->n_dicts_max)
	gt1_double (psc->gt1_dict_stack, Gt1Dict *, psc->n_dicts_max);
      psc->gt1_dict_stack[psc->n_dicts++] = dict;
      psc->n_values--;
    }
}

static void
internal_end (Gt1PSContext *psc)
{
  /* note: this magic constant changes if we separate out the internal
     dict from the user one; in fact, GhostScript uses three. */
  if (psc->n_dicts == 1)
    {
      printf ("dict stack underflow\n");
      psc->quit = 1;
    }
  psc->n_dicts--;
}

static void
internal_dup (Gt1PSContext *psc)
{
  if (psc->n_values == 0)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
  else
    {
      ensure_stack (psc, 1);
      psc->value_stack[psc->n_values] =
	psc->value_stack[psc->n_values - 1];
      psc->n_values++;
    }
}

static void
internal_pop (Gt1PSContext *psc)
{
  if (psc->n_values == 0)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
  else
      psc->n_values--;
}

static void
internal_exch (Gt1PSContext *psc)
{
  Gt1Value tmp;
  int stack_size;

  stack_size = psc->n_values;
  if (stack_size < 2)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
  else
    {
      tmp = psc->value_stack[stack_size - 2];
      psc->value_stack[stack_size - 2] = 
	psc->value_stack[stack_size - 1];
      psc->value_stack[stack_size - 1] = tmp;
    }
}

/* this doesn't do anything - we don't enforce readonly */
static void
internal_readonly (Gt1PSContext *psc)
{
  if (psc->n_values == 0)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
}

/* this doesn't do anything - we don't enforce executeonly */
static void
internal_executeonly (Gt1PSContext *psc)
{
  if (psc->n_values == 0)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
}

/* this doesn't do anything - we don't enforce noaccess */
static void
internal_noaccess (Gt1PSContext *psc)
{
  if (psc->n_values == 0)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
}

static void
internal_def (Gt1PSContext *psc)
{
  Gt1NameId key;
  Gt1Dict *dict;

  if (get_stack_name (psc, &key, 2))
    {
      dict = psc->gt1_dict_stack[psc->n_dicts - 1];
      gt1_dict_def (psc->r, dict, key, &psc->value_stack[psc->n_values - 1]);
      psc->n_values -= 2;
    }
}

static void
internal_false (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_BOOL;
  psc->value_stack[psc->n_values].val.bool_val = gt1_false;
  psc->n_values++;
}

static void
internal_true (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_BOOL;
  psc->value_stack[psc->n_values].val.bool_val = gt1_true;
  psc->n_values++;
}

static void
internal_StandardEncoding (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  /* todo: push actual encoding array */
  psc->value_stack[psc->n_values].type = GT1_VAL_NUM;
  psc->value_stack[psc->n_values].val.num_val = 42;
  psc->n_values++;
}

static void
internalop_openbracket (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_MARK;
  psc->n_values++;
}

static void
internalop_closebracket (Gt1PSContext *psc)
{
  int i;
  Gt1Array *array;
  int size, start_idx;

  for (i = psc->n_values - 1; i >= 0; i--)
    if (psc->value_stack[i].type == GT1_VAL_MARK)
      break;
  if (psc->value_stack[i].type != GT1_VAL_MARK)
    {
      printf ("unmatched mark\n");
      psc->quit = 1;
    }
  start_idx = i + 1;
  size = psc->n_values - start_idx;
  array = array_new (psc->r, size);
  for (i = 0; i < size; i++)
    array->vals[i] = psc->value_stack[start_idx + i];
  psc->n_values -= size;
  psc->value_stack[psc->n_values - 1].type = GT1_VAL_ARRAY;
  psc->value_stack[psc->n_values - 1].val.array_val = array;
}

static void
internal_currentdict (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_DICT;
  psc->value_stack[psc->n_values].val.dict_val =
    psc->gt1_dict_stack[psc->n_dicts - 1];
  psc->n_values++;
}

static void
internal_currentfile (Gt1PSContext *psc)
{
  /* todo: we want to move away from a tc pointer towards tags, to avoid
     potential security holes from misusing these data structures */
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_FILE;
  psc->value_stack[psc->n_values].val.file_val = psc->tc;
  psc->n_values++;
}

#define EEXEC_C1 ((unsigned short)52845)
#define EEXEC_C2 ((unsigned short)22719)

/* return number of bytes in result */
static int
decrypt_eexec (char *plaintext, const char *ciphertext, int ciphertext_size)
{
  int i;
  unsigned short r;
  unsigned char cipher;
  unsigned char plain;

  r = 55665;  /* initial key */

  for (i = 0; i < ciphertext_size; i++)
    {
      cipher = ciphertext[i];
      plain = (cipher ^ (r>>8));
      r = (cipher + r) * EEXEC_C1 + EEXEC_C2;
      if (i >= 4)
	plaintext[i - 4] = plain;
    }
  return ciphertext_size - 4;
}

/* this one is great fun! */
static void
internal_eexec (Gt1PSContext *psc)
{
  Gt1TokenContext *file_tc;
  char *ciphertext;
  int ciphertext_size, ciphertext_size_max;
  char *plaintext;
  int plaintext_size;
  int num_nulls;
  int byte;
  MyGt1String string;
  Gt1TokenContext *new_tc;

  if (get_stack_file (psc, &file_tc, 1))
    {
      psc->n_values--;

      /* first, suck the encrypted stream from the specified file */
      ciphertext_size = 0;
      ciphertext_size_max = 512;
      ciphertext = gt1_new (char, ciphertext_size_max);
      num_nulls = 0;
      while (num_nulls < 16)
	{
	  if (ciphertext_size == ciphertext_size_max)
	    gt1_double (ciphertext, char, ciphertext_size_max);
	  byte = tokenize_get_hex_byte (file_tc);
	  if (byte < 0)
	    {
	      printf ("eexec input appears to be truncated\n");
	      psc->quit = 1;
	      return;
	    }
	  if (byte == 0)
	    num_nulls++;
	  else
	    num_nulls = 0;
	  ciphertext[ciphertext_size++] = byte;
	}

      /* then, decrypt it */
      plaintext = gt1_new (char, ciphertext_size);
      plaintext_size = decrypt_eexec (plaintext, ciphertext, ciphertext_size);
      gt1_free (ciphertext);

#if 1 && defined(VERBOSE)
      fwrite (plaintext, 1, plaintext_size, stdout);
#endif

      /* finally, create a new Gt1TokenContext for the string, and switch
	 to executing it. */
      string.start = plaintext;
      string.fin = plaintext + plaintext_size;
      new_tc = tokenize_new_from_mystring (&string);
      gt1_free (plaintext);

      if (psc->n_files_max == psc->n_files)
	{
	  printf ("overflow of file stack\n");
	  psc->quit = 1;
	  return;
	}
      psc->file_stack[psc->n_files++] = new_tc;
      psc->tc = new_tc;

      /* alternatively, we could have recursively called the PostScript
	 evaluation loop from here, but this seems to be just as good. */
    }
}

static void
internal_array (Gt1PSContext *psc)
{
  Gt1Array *array;
  double d_size;

  if (get_stack_number (psc, &d_size, 1))
    {
      array = array_new (psc->r, d_size);
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_ARRAY;
      psc->value_stack[psc->n_values - 1].val.array_val = array;
    }
}

static void
internal_string (Gt1PSContext *psc)
{
  Gt1String string;
  double d_size;
  int size;

  if (get_stack_number (psc, &d_size, 1))
    {
      size = d_size;
      string.start = gt1_region_alloc (psc->r, size);
      string.size = size;
      memset (string.start, 0, size);
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_STR;
      psc->value_stack[psc->n_values - 1].val.str_val = string;
    }
}

static void
internal_readstring (Gt1PSContext *psc)
{
  Gt1String string;
  Gt1TokenContext *file_tc;

  if (get_stack_string (psc, &string, 1) &&
      get_stack_file (psc, &file_tc, 2))
    {
      tokenize_get_raw (file_tc, string.start, string.size);
      psc->value_stack[psc->n_values - 2].type = GT1_VAL_STR;
      psc->value_stack[psc->n_values - 2].val.str_val = string;
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL;
      psc->value_stack[psc->n_values - 1].val.bool_val = gt1_true;
    }
}

static void
internal_put (Gt1PSContext *psc)
{
  Gt1Array *array;
  double d_index;
  int index;
  Gt1Dict *dict;
  Gt1NameId key;

  if (psc->n_values >= 3 &&
      psc->value_stack[psc->n_values - 3].type == GT1_VAL_DICT &&
      get_stack_name (psc, &key, 2))
    {
      /* dict key val   put  -- */
      get_stack_dict (psc, &dict, 3);
      gt1_dict_def (psc->r, dict, key, &psc->value_stack[psc->n_values - 1]);
      psc->n_values -= 3;
    }
  else if (psc->n_values >= 3 &&
	   psc->value_stack[psc->n_values - 3].type == GT1_VAL_PROC &&
	   get_stack_number (psc, &d_index, 2))
    {
      array = psc->value_stack[psc->n_values - 3].val.proc_val;
      index = d_index;
      if (index < 0 || index >= array->n_values)
	{
	  printf ("range check\n");
	  psc->quit = 1;
	  return;
	}
      array->vals[index] = psc->value_stack[psc->n_values - 1];
      psc->n_values -= 3;
    }
  else if (psc->n_values >= 3 &&
	   get_stack_array (psc, &array, 3) &&
	   get_stack_number (psc, &d_index, 2))
    {
      /* array index val  put  -- */
      index = d_index;
      if (index < 0 || index >= array->n_values)
	{
	  printf ("range check\n");
	  psc->quit = 1;
	  return;
	}
      array->vals[index] = psc->value_stack[psc->n_values - 1];
      psc->n_values -= 3;
    }
}

static void
internal_get (Gt1PSContext *psc)
{
  Gt1Array *array;
  double d_index;
  int index;
  Gt1Dict *dict;
  Gt1NameId key;
  Gt1Value *val;

  if (psc->n_values >= 2 &&
      psc->value_stack[psc->n_values - 2].type == GT1_VAL_DICT &&
      get_stack_name (psc, &key, 1))
    {
      /* dict key    get  val */
      get_stack_dict (psc, &dict, 2);
      val = gt1_dict_lookup (dict, key);
      if (val == NULL)
	{
	  printf ("key not found\n");
	  psc->quit = 1;
	  return;
	}
#ifdef VERBOSE
      printf ("value: ");
      print_value (psc, val);
      printf ("\n");
#endif
      psc->n_values -= 1;
      psc->value_stack[psc->n_values - 1] = *val;
    }
  else if (psc->n_values >= 2 &&
	   psc->value_stack[psc->n_values - 2].type == GT1_VAL_PROC &&
	   get_stack_number (psc, &d_index, 1))
    {
      /* array index  get  val */
      array = psc->value_stack[psc->n_values - 2].val.proc_val;
      index = d_index;
      if (index < 0 || index >= array->n_values)
	{
	  printf ("range check\n");
	  psc->quit = 1;
	  return;
	}
      psc->n_values -= 1;
      psc->value_stack[psc->n_values - 1] = array->vals[index];
    }
  else if (get_stack_array (psc, &array, 2) &&
	   get_stack_number (psc, &d_index, 1))
    {
      /* array index  get  val */
      index = d_index;
      if (index < 0 || index >= array->n_values)
	{
	  printf ("range check\n");
	  psc->quit = 1;
	  return;
	}
      psc->n_values -= 1;
      psc->value_stack[psc->n_values - 1] = array->vals[index];
    }
}

static void
internal_index (Gt1PSContext *psc)
{
  double d_index;
  int index;

  if (get_stack_number (psc, &d_index, 1))
    {
      index = d_index;
      if (index < 0 || index > psc->n_values - 2)
	{
	  printf ("index range check\n");
	  psc->quit = 1;
	  return;
	}
      psc->value_stack[psc->n_values - 1] =
	psc->value_stack[psc->n_values - (index + 2)];
    }
}

static void
internal_definefont (Gt1PSContext *psc)
{
  Gt1NameId key;
  Gt1Dict *dict;

  if (psc->n_values < 2)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
  else if (get_stack_name (psc, &key, 2))
    {
      dict = psc->fonts;
      gt1_dict_def (psc->r, dict, key, &psc->value_stack[psc->n_values - 1]);
#ifdef VERBOSE
      print_value_deep (psc, &psc->value_stack[psc->n_values - 1], 0);
#endif
      psc->n_values -= 1;
    }
}

static void
internal_mark (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_MARK;
  psc->n_values++;
}

static void
internal_closefile (Gt1PSContext *psc)
{
  Gt1TokenContext *tc;

  if (get_stack_file (psc, &tc, 1))
    {
      if (psc->n_files == 1)
	{
	  printf ("file stack underflow\n");
	  psc->quit = 1;
	}
      else if (psc->file_stack[psc->n_files - 1] == tc)
	{
	  /* pop the file stack */
	  tokenize_free (psc->tc);
	  psc->n_files--;
	  psc->tc = psc->file_stack[psc->n_files - 1];
	  psc->n_values--;
	}
      else
	{
	  printf ("closefile: whoa, file cowboy!\n");
	  psc->quit = 1;
	}
    }
}

static void
internal_cleartomark (Gt1PSContext *psc)
{
  int i;

  for (i = psc->n_values - 1; i >= 0; i--)
    if (psc->value_stack[i].type == GT1_VAL_MARK)
      break;
  if (psc->value_stack[i].type != GT1_VAL_MARK)
    {
      printf ("cleartomark: unmatched mark\n");
      psc->quit = 1;
    }
  psc->n_values = i;
}

static void
internal_systemdict (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_DICT;
  psc->value_stack[psc->n_values].val.dict_val =
    psc->gt1_dict_stack[0];
  psc->n_values++;
}

static void
internal_userdict (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_DICT;
  psc->value_stack[psc->n_values].val.dict_val =
    psc->gt1_dict_stack[2];
  psc->n_values++;
}

static void
internal_known (Gt1PSContext *psc)
{
  Gt1NameId key;
  Gt1Dict *dict;
  int known;

  if (psc->n_values >= 2 &&
      get_stack_dict (psc, &dict, 2) &&
      get_stack_name (psc, &key, 1))
    {
      known = (gt1_dict_lookup (dict, key) != 0);
      psc->n_values -= 1;
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL;
      psc->value_stack[psc->n_values - 1].val.bool_val = known;
    }
}

static void
internal_ifelse (Gt1PSContext *psc)
{
  Gt1Proc *proc1, *proc2;
  int bool;

  if (psc->n_values >= 3 &&
      get_stack_bool (psc, &bool, 3) &&
      get_stack_proc (psc, &proc1, 2) &&
      get_stack_proc (psc, &proc2, 1))
    {
      psc->n_values -= 3;
      if (bool)
	eval_proc (psc, proc1);
      else
	eval_proc (psc, proc2);
    }
}

static void
internal_if (Gt1PSContext *psc)
{
  Gt1Proc *proc;
  int bool;

  if (psc->n_values >= 2 &&
      get_stack_bool (psc, &bool, 2) &&
      get_stack_proc (psc, &proc, 1))
    {
      psc->n_values -= 2;
      if (bool)
	eval_proc (psc, proc);
    }
}

static void
internal_for (Gt1PSContext *psc)
{
  double initial, increment, limit;
  Gt1Proc *proc;
  double val;

  if (psc->n_values >= 4 &&
      get_stack_number (psc, &initial, 4) &&
      get_stack_number (psc, &increment, 3) &&
      get_stack_number (psc, &limit, 2) &&
      get_stack_proc (psc, &proc, 1))
    {
      psc->n_values -= 4;
      for (val = initial; !psc->quit &&
	     (increment > 0 ? (val <= limit) : (val >= limit));
	   val += increment)
	{
	  ensure_stack (psc, 1);
	  psc->value_stack[psc->n_values].type = GT1_VAL_NUM;
	  psc->value_stack[psc->n_values].val.num_val = val;
	  psc->n_values++;
	  eval_proc (psc, proc);
	}
    }
}

static void
internal_not (Gt1PSContext *psc)
{
  int bool;

  if (psc->n_values >= 1 &&
      get_stack_bool (psc, &bool, 1))
    {
      psc->value_stack[psc->n_values - 1].val.bool_val = !bool;
    }
}

static void
internal_bind (Gt1PSContext *psc)
{
  Gt1Proc *proc;

  if (psc->n_values >= 1 &&
      get_stack_proc (psc, &proc, 1))
    {
      /* todo: implement, when procs become normal values */
    }
}

static void
internal_exec (Gt1PSContext *psc)
{
  Gt1Proc *proc;

  if (psc->n_values >= 1 &&
      get_stack_proc (psc, &proc, 1))
    {
      psc->n_values -= 1;
      eval_proc (psc, proc);
    }
}

static void
internal_count (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_NUM;
  psc->value_stack[psc->n_values].val.num_val = psc->n_values;
  psc->n_values++;
}

static void
internal_eq (Gt1PSContext *psc)
{
  double a, b;
  Gt1NameId na, nb;

  if (psc->n_values < 2)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
      return;
    }
  if (psc->value_stack[psc->n_values - 1].type == GT1_VAL_NAME &&
      get_stack_name (psc, &na, 2) &&
      get_stack_name (psc, &nb, 1))
    {
      psc->n_values -= 1;
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL;
      psc->value_stack[psc->n_values - 1].val.bool_val = (na == nb);
    }
  else if (get_stack_number (psc, &a, 2) &&
      get_stack_number (psc, &b, 1))
    {
      psc->n_values -= 1;
      psc->value_stack[psc->n_values - 1].type = GT1_VAL_BOOL;
      psc->value_stack[psc->n_values - 1].val.bool_val = (a == b);
    }
}

static void
internal_ne (Gt1PSContext *psc)
{
  internal_eq (psc);
  if (!psc->quit)
    psc->value_stack[psc->n_values - 1].val.bool_val =
      !psc->value_stack[psc->n_values - 1].val.bool_val;
}

static void
internal_type (Gt1PSContext *psc)
{
  Gt1ValueType type;

  if (psc->n_values >= 1)
    {
      type = psc->value_stack[psc->n_values - 1].type;
      if (type == GT1_VAL_NUM)
	{
	  psc->value_stack[psc->n_values - 1].type = GT1_VAL_NAME;
	  psc->value_stack[psc->n_values - 1].val.name_val =
	    gt1_name_context_intern (psc->nc, "integertype");
	}
      else
	{
	  printf ("type not fully implemented");
	}
    }
}

static void
internal_cvx (Gt1PSContext *psc)
{
  Gt1Value *val;

  if (psc->n_values < 1)
    {
      printf ("stack underflow\n");
      psc->quit = 1;
    }
  else
    {
      val = &psc->value_stack[psc->n_values - 1];
      if (val->type == GT1_VAL_NAME)
	val->type = GT1_VAL_UNQ_NAME;
      else if (val->type == GT1_VAL_ARRAY)
	val->type = GT1_VAL_PROC;
      else
	{
	  printf ("warning: cvx called on ");
	  print_value (psc, val);
	  printf ("\n");
	}
    }
}

static void
internal_matrix (Gt1PSContext *psc)
{
  Gt1Array *array;
  int i;

  array = array_new (psc->r, 6);
  for (i = 0; i < 6; i++)
    {
      array->vals[i].type = GT1_VAL_NUM;
      array->vals[i].val.num_val = (i == 0 || i == 3);
    }
  psc->value_stack[psc->n_values].type = GT1_VAL_ARRAY;
  psc->value_stack[psc->n_values].val.array_val = array;
  psc->n_values++;
}

static void
internal_FontDirectory (Gt1PSContext *psc)
{
  ensure_stack (psc, 1);
  psc->value_stack[psc->n_values].type = GT1_VAL_DICT;
  psc->value_stack[psc->n_values].val.dict_val = psc->fonts;
  psc->n_values++;
}

/* the table of internal procedures */

typedef struct _InternalGt1ProcListing {
  char *name;
  void (*function) (Gt1PSContext *psc);
} InternalGt1ProcListing;

InternalGt1ProcListing internal_procs[] = {
  { "dict", internal_dict },
  { "begin", internal_begin },
  { "end", internal_end },
  { "dup", internal_dup },
  { "pop", internal_pop },
  { "exch", internal_exch },
  { "readonly", internal_readonly },
  { "executeonly", internal_executeonly },
  { "noaccess", internal_noaccess },
  { "def", internal_def },
  { "false", internal_false },
  { "true", internal_true },
  { "StandardEncoding", internal_StandardEncoding },
  { "[", internalop_openbracket },
  { "]", internalop_closebracket },
  { "currentdict", internal_currentdict },
  { "currentfile", internal_currentfile },
  { "eexec", internal_eexec },
  { "array", internal_array },
  { "string", internal_string },
  { "readstring", internal_readstring },
  { "put", internal_put },
  { "get", internal_get },
  { "index", internal_index },
  { "definefont", internal_definefont },
  { "mark", internal_mark },
  { "closefile", internal_closefile },
  { "cleartomark", internal_cleartomark },
  { "systemdict", internal_systemdict },
  { "userdict", internal_userdict },
  { "known", internal_known },
  { "ifelse", internal_ifelse },
  { "if", internal_if },
  { "for", internal_for },
  { "not", internal_not },
  { "bind", internal_bind },
  { "exec", internal_exec },
  { "count", internal_count },
  { "eq", internal_eq },
  { "ne", internal_ne },
  { "type", internal_type },
  { "cvx", internal_cvx },
  { "matrix", internal_matrix },
  { "FontDirectory", internal_FontDirectory }
};

/* here end the internal procedures */

static Gt1PSContext *
pscontext_new (Gt1TokenContext *tc)
{
  Gt1PSContext *psc;
  Gt1Dict *systemdict;
  Gt1Dict *globaldict;
  Gt1Dict *userdict;
  int i;
  Gt1Value val;

  psc = gt1_new (Gt1PSContext, 1);
  psc->r = gt1_region_new ();
  psc->tc = tc;

  psc->nc = gt1_name_context_new ();

  psc->n_values = 0;
  psc->n_values_max = 16;
  psc->value_stack = gt1_new (Gt1Value, psc->n_values_max);

  psc->n_dicts_max = 16;
  psc->gt1_dict_stack = gt1_new (Gt1Dict *, psc->n_dicts_max);

  systemdict = gt1_dict_new (psc->r, sizeof(internal_procs) /
			 sizeof(InternalGt1ProcListing));
  for (i = 0; i < sizeof(internal_procs) / sizeof(InternalGt1ProcListing); i++)
    {
      val.type = GT1_VAL_INTERNAL;
      val.val.internal_val = internal_procs[i].function;
      gt1_dict_def (psc->r, systemdict,
		    gt1_name_context_intern (psc->nc,
					     internal_procs[i].name),
		    &val);
    }
  psc->gt1_dict_stack[0] = systemdict;
  globaldict = gt1_dict_new (psc->r, 16);
  psc->gt1_dict_stack[1] = globaldict;
  userdict = gt1_dict_new (psc->r, 16);
  psc->gt1_dict_stack[2] = userdict;
  psc->n_dicts = 3;

  psc->fonts = gt1_dict_new (psc->r, 1);

  psc->n_files_max = 16;
  psc->file_stack = gt1_new (Gt1TokenContext *, psc->n_files_max);
  psc->file_stack[0] = tc;
  psc->n_files = 1;

  psc->quit = 0;
  return psc;
}

static void
pscontext_free (Gt1PSContext *psc)
{
  /* Empty the value stack.  */
  while (psc->n_values > 0) internal_pop (psc);
  gt1_free (psc->value_stack);

  gt1_free  (psc->file_stack);
  gt1_free  (psc->gt1_dict_stack);

  gt1_name_context_free (psc->nc);
  gt1_region_free (psc->r);

  gt1_free (psc);
}



static void
eval_executable (Gt1PSContext *psc, Gt1Value *val)
{
  switch (val->type)
    {
    case GT1_VAL_INTERNAL:
      val->val.internal_val (psc);
      break;
    case GT1_VAL_PROC:
      eval_proc (psc, val->val.proc_val);
      break;
    default:
      ensure_stack (psc, 1);
      psc->value_stack[psc->n_values] = *val;
      psc->n_values++;
      break;
    }
}

/* Scan a value from the psc's token context - usually one token, or
   a proc. Return the token type. */
static TokenType
parse_ps_token (Gt1PSContext *psc, Gt1Value *val)
{
  MyGt1String lexeme;
  TokenType type;
  Gt1Proc *proc;
  int n_proc, n_proc_max;

  type = tokenize_get (psc->tc, &lexeme);
  switch (type)
    {
    case TOK_NUM:
      val->type = GT1_VAL_NUM;
      val->val.num_val = parse_num (&lexeme);
      break;
    case TOK_NAME:
      val->type = GT1_VAL_NAME;
      val->val.name_val =
	gt1_name_context_intern_size (psc->nc, lexeme.start,
				  lexeme.fin - lexeme.start);
				  break;
    case TOK_STR:
      val->type = GT1_VAL_STR;
      /* todo: processing of escape characters */
      val->val.str_val.start = lexeme.start;
      val->val.str_val.size = lexeme.fin - lexeme.start;
      break;
    case TOK_IDENT:
      val->type = GT1_VAL_UNQ_NAME;
      val->val.name_val =
	gt1_name_context_intern_size (psc->nc, lexeme.start,
				  lexeme.fin - lexeme.start);
      break;
    case TOK_OPENBRACE:
      n_proc = 0;
      n_proc_max = 16;
      proc = (Gt1Proc *)gt1_region_alloc (psc->r, sizeof(Gt1Proc) +
				   (n_proc_max - 1) *
				   sizeof(Gt1Value));
      while (1)
	{
	  if (n_proc == n_proc_max)
	    {
	      int old_size;

	      old_size = sizeof(Gt1Proc) + (n_proc_max - 1) * sizeof(Gt1Value);
	      n_proc_max <<= 1;

	      proc = (Gt1Proc *)gt1_region_realloc (psc->r, proc,
					     old_size,
					     sizeof(Gt1Proc) +
					     (n_proc_max - 1) *
					     sizeof(Gt1Value));
	    }
	  if (parse_ps_token (psc, &proc->vals[n_proc]) == TOK_CLOSEBRACE ||
	      psc->quit)
	    break;
	  n_proc++;
	}
      proc->n_values = n_proc;
      val->type = GT1_VAL_PROC;
      val->val.proc_val = proc;
      break;
    case TOK_CLOSEBRACE:
    case TOK_END:
      break;
    default:
      printf ("unimplemented token type\n");
      psc->quit = 1;
      break;
    }
  return type;
}

static void
eval_ps_val (Gt1PSContext *psc, Gt1Value *val)
{
  Gt1Value *new_val;

#ifdef VERBOSE
  print_value (psc, val);
  printf ("\n");
#endif
  switch (val->type)
    {
    case GT1_VAL_NUM:
    case GT1_VAL_BOOL:
    case GT1_VAL_STR:
    case GT1_VAL_NAME:
    case GT1_VAL_ARRAY:
    case GT1_VAL_PROC:
    case GT1_VAL_DICT:
      ensure_stack (psc, 1);
      psc->value_stack[psc->n_values] = *val;
      psc->n_values++;
      break;
    case GT1_VAL_UNQ_NAME:
      new_val = gt1_dict_stack_lookup (psc, val->val.name_val);
      if (new_val != NULL)
	eval_executable (psc, new_val);
      else
	{
	  printf ("undefined identifier ");
	  print_value (psc, val);
	  putchar ('\n');
	  psc->quit = 1;
	}
      break;
    case GT1_VAL_INTERNAL:
      val->val.internal_val (psc);
      break;
    default:
      printf ("value not handled\n");
      psc->quit = 1;
      break;
    }
#ifdef VERBOSE
  if (!psc->quit)
    {
      printf ("   ");
      print_stack (psc);
    }
#endif
}

/* maybe we should return the dict; easier to handle */
static Gt1PSContext *
eval_ps (Gt1TokenContext *tc)
{
  TokenType type;
  Gt1PSContext *psc;
  Gt1Value val;

  psc = pscontext_new (tc);
  do {
    type = parse_ps_token (psc, &val);
    if (type == TOK_END) break;
    if (type == TOK_CLOSEBRACE)
      {
	printf ("unexpected close brace\n");
	break;
      }
    eval_ps_val (psc, &val);
  } while (!psc->quit);

  return psc;
}

/* This routine _assumes_ that plaintext is passed in with enough
   space to hold the decrypted text */
static void
charstring_decrypt (Gt1String *plaintext, Gt1String *ciphertext)
{
  int ciphertext_size;
  int i;
  unsigned short r;
  unsigned char cipher;
  unsigned char plain;

  ciphertext_size = ciphertext->size;
  if (plaintext->size < ciphertext_size - 4)
    {
      printf ("not enough space allocated for charstring decryption\n");
      return;
    }

  r = 4330;  /* initial key */

  for (i = 0; i < ciphertext_size; i++)
    {
      cipher = ciphertext->start[i];
      plain = (cipher ^ (r>>8));
      r = (cipher + r) * EEXEC_C1 + EEXEC_C2;
      if (i >= 4)
	plaintext->start[i - 4] = plain;
    }
  plaintext->size = ciphertext->size - 4;
}

#ifdef VERBOSE
static void
print_glyph_code (Gt1String *glyph_code)
{
  Gt1String plaintext;
  int i;
  int byte, byte1, byte2, byte3, byte4;

  plaintext.start = gt1_alloc (glyph_code->size);
  plaintext.size = glyph_code->size;
  charstring_decrypt (&plaintext, glyph_code);

  for (i = 0; i < plaintext.size; i++)
    {
      byte = ((unsigned char *)plaintext.start)[i];
      if (byte >= 32 && byte <= 246)
	printf (" %d", byte - 139);
      else if (byte >= 247 && byte <= 250)
	{
	  byte1 = ((unsigned char *)plaintext.start)[++i];
	  printf (" %d", ((byte - 247) << 8) + byte1 + 108);
	}
      else if (byte >= 251 && byte <= 254)
	{
	  byte1 = ((unsigned char *)plaintext.start)[++i];
	  printf (" %d", -((byte - 251) << 8) - byte1 - 108);
	}
      else if (byte == 255)
	{
	  byte1 = ((unsigned char *)plaintext.start)[++i];
	  byte2 = ((unsigned char *)plaintext.start)[++i];
	  byte3 = ((unsigned char *)plaintext.start)[++i];
	  byte4 = ((unsigned char *)plaintext.start)[++i];
	  /* warning: this _must_ be a 32 bit int - alpha portability
	     issue! */
	  printf (" %d", (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4);
	}
      else if (byte == 12)
	{
	  byte1 = ((unsigned char *)plaintext.start)[++i];
	  if (byte1 == 6)
	    printf (" seac");
	  else if (byte1 == 7)
	    printf (" sbw");
	  else if (byte1 == 0)
	    printf (" dotsection");
	  else if (byte1 == 2)
	    printf (" hstem3");
	  else if (byte1 == 1)
	    printf (" vstem3");
	  else if (byte1 == 12)
	    printf (" div");
	  else if (byte1 == 16)
	    printf (" callothersubr");
	  else if (byte1 == 17)
	    printf (" pop");
	  else if (byte1 == 33)
	    printf (" setcurrentpoint");
	  else
	    printf (" esc%d", byte1);
	}
      else if (byte == 14)
	printf (" endchar");
      else if (byte == 13)
	printf (" hsbw");
      else if (byte == 9)
	printf (" closepath");
      else if (byte == 6)
	printf (" hlineto");
      else if (byte == 22)
	printf (" hmoveto");
      else if (byte == 31)
	printf (" hvcurveto");
      else if (byte == 5)
	printf (" rlineto");
      else if (byte == 21)
	printf (" rmoveto");
      else if (byte == 8)
	printf (" rrcurveto");
      else if (byte == 30)
	printf (" vhcurveto");
      else if (byte == 7)
	printf (" vlineto");
      else if (byte == 4)
	printf (" vmoveto");
      else if (byte == 1)
	printf (" hstem");
      else if (byte == 3)
	printf (" vstem");
      else if (byte == 10)
	printf (" callsubr");
      else if (byte == 11)
	printf (" return");
      else
	printf (" com%d", byte);
    }
  printf ("\n");
  gt1_free (plaintext.start);
}
#endif

/* Gt1Dict is the toplevel font dict. This allocates a new string for the
   plaintext and stores it in body. */
static void
get_subr_body (Gt1PSContext *psc, Gt1String *body, Gt1Dict *fontdict, int subr)
{
  Gt1Value *private_val;
  Gt1Value *subrs_val;
  Gt1Array *subrs_array;
  Gt1String *ciphertext;

  private_val = gt1_dict_lookup (fontdict,
			     gt1_name_context_intern (psc->nc, "Private"));
  if (private_val == NULL)
    {
      printf ("No Private array\n");
      return;
    }
  subrs_val = gt1_dict_lookup (private_val->val.dict_val,
			   gt1_name_context_intern (psc->nc, "Subrs"));
  if (subrs_val == NULL)
    {
      printf ("No Subrs array\n");
      return;
    }
  subrs_array = subrs_val->val.array_val;
  /* more type & range checking */
  ciphertext = &subrs_array->vals[subr].val.str_val;
  body->start = gt1_alloc (ciphertext->size);
  body->size = ciphertext->size;
  charstring_decrypt (body, ciphertext);
}

typedef struct _BezState {
  ArtBpath *bezpath;
  int size_bezpath, size_bezpath_max;
  
/* this is designed to do compression of a string of moveto's in a row. */
  int need_moveto;

  double x, y; /* current point */
  double x0, y0; /* beginning of subpath */
} BezState;

static BezState *
bs_new (void)
{
  BezState *bs;

  bs = gt1_new (BezState, 1);

  bs->size_bezpath = 0;
  bs->size_bezpath_max = 16;
  bs->bezpath = gt1_new (ArtBpath, bs->size_bezpath_max);

  bs->x = 0;
  bs->y = 0;
  bs->x0 = 0;
  bs->y0 = 0;
  bs->need_moveto = 1;

  return bs;
}

static void
bs_moveto (BezState *bs, double x, double y)
{
  bs->x = x;
  bs->y = y;
  bs->need_moveto = 1;
}

static void
bs_rmoveto (BezState *bs, double dx, double dy)
{
  bs->x += dx;
  bs->y += dy;
  bs->need_moveto = 1;
}

static void
bs_do_moveto (BezState *bs)
{
  ArtBpath *bezpath;
  int size_bezpath;

  if (bs->need_moveto)
    { 
      bezpath = bs->bezpath;
      size_bezpath = bs->size_bezpath;

      if (size_bezpath == bs->size_bezpath_max)
	{
	  gt1_double (bezpath, ArtBpath, bs->size_bezpath_max);
	  bs->bezpath = bezpath;
	}
      bezpath[size_bezpath].code = ART_MOVETO;
      bezpath[size_bezpath].x1 = 0;
      bezpath[size_bezpath].y1 = 0;
      bezpath[size_bezpath].x2 = 0;
      bezpath[size_bezpath].y2 = 0;
      bezpath[size_bezpath].x3 = bs->x;
      bezpath[size_bezpath].y3 = bs->y;
      bs->size_bezpath++;
      bs->x0 = bs->x;
      bs->y0 = bs->y;
      bs->need_moveto = 0;
    }
}

static void
bs_rlineto (BezState *bs, double dx, double dy)
{
  ArtBpath *bezpath;
  int size_bezpath;

  bs_do_moveto (bs);

  bezpath = bs->bezpath;
  size_bezpath = bs->size_bezpath;

  if (size_bezpath == bs->size_bezpath_max)
    {
      gt1_double (bezpath, ArtBpath, bs->size_bezpath_max);
      bs->bezpath = bezpath;
    }
  bezpath[size_bezpath].code = ART_LINETO;
  bezpath[size_bezpath].x1 = 0;
  bezpath[size_bezpath].y1 = 0;
  bezpath[size_bezpath].x2 = 0;
  bezpath[size_bezpath].y2 = 0;
  bs->x += dx;
  bs->y += dy;
  bezpath[size_bezpath].x3 = bs->x;
  bezpath[size_bezpath].y3 = bs->y;
  bs->size_bezpath++;
}

static void
bs_rcurveto (BezState *bs,
	     double dx1, double dy1,
	     double dx2, double dy2,
	     double dx3, double dy3)
{
  ArtBpath *bezpath;
  int size_bezpath;
  double x, y;

  bs_do_moveto (bs);

  bezpath = bs->bezpath;
  size_bezpath = bs->size_bezpath;

  if (size_bezpath == bs->size_bezpath_max)
    {
      gt1_double (bezpath, ArtBpath, bs->size_bezpath_max);
      bs->bezpath = bezpath;
    }
  bezpath[size_bezpath].code = ART_CURVETO;
  x = bs->x + dx1;
  y = bs->y + dy1;
  bezpath[size_bezpath].x1 = x;
  bezpath[size_bezpath].y1 = y;
  x += dx2;
  y += dy2;
  bezpath[size_bezpath].x2 = x;
  bezpath[size_bezpath].y2 = y;
  x += dx3;
  y += dy3;
  bezpath[size_bezpath].x3 = x;
  bezpath[size_bezpath].y3 = y;
  bs->x = x;
  bs->y = y;
  bs->size_bezpath++;
}

/* this is for callothersubr (2) */
static void
bs_curveto (BezState *bs,
	    double flexbuf[6])
{
  ArtBpath *bezpath;
  int size_bezpath;

  bs->need_moveto = 0;

  bezpath = bs->bezpath;
  size_bezpath = bs->size_bezpath;

  if (size_bezpath == bs->size_bezpath_max)
    {
      gt1_double (bezpath, ArtBpath, bs->size_bezpath_max);
      bs->bezpath = bezpath;
    }
  bezpath[size_bezpath].code = ART_CURVETO;
  bezpath[size_bezpath].x1 = flexbuf[0];
  bezpath[size_bezpath].y1 = flexbuf[1];
  bezpath[size_bezpath].x2 = flexbuf[2];
  bezpath[size_bezpath].y2 = flexbuf[3];
  bezpath[size_bezpath].x3 = flexbuf[4];
  bezpath[size_bezpath].y3 = flexbuf[5];
  bs->size_bezpath++;
}

static void
bs_closepath (BezState *bs)
{
  ArtBpath *bezpath;
  int size_bezpath;

  if (bs->x0 == bs->x && bs->y0 == bs->y)
    return;

  bezpath = bs->bezpath;
  size_bezpath = bs->size_bezpath;

  if (size_bezpath == bs->size_bezpath_max)
    {
      gt1_double (bezpath, ArtBpath, bs->size_bezpath_max);
      bs->bezpath = bezpath;
    }
  bezpath[size_bezpath].code = ART_LINETO;
  bezpath[size_bezpath].x1 = 0;
  bezpath[size_bezpath].y1 = 0;
  bezpath[size_bezpath].x2 = 0;
  bezpath[size_bezpath].y2 = 0;
  bezpath[size_bezpath].x3 = bs->x0;
  bezpath[size_bezpath].y3 = bs->y0;
  bs->size_bezpath++;
}

static ArtBpath *
bs_end (BezState *bs)
{
  ArtBpath *bezpath;
  int size_bezpath;

  bezpath = bs->bezpath;
  size_bezpath = bs->size_bezpath;

  if (size_bezpath == bs->size_bezpath_max)
    gt1_double (bezpath, ArtBpath, bs->size_bezpath_max);
  bezpath[size_bezpath].code = ART_END;
  bezpath[size_bezpath].x1 = 0;
  bezpath[size_bezpath].y1 = 0;
  bezpath[size_bezpath].x2 = 0;
  bezpath[size_bezpath].y2 = 0;
  bezpath[size_bezpath].x3 = 0;
  bezpath[size_bezpath].y3 = 0;

  gt1_free (bs);

  return bezpath;
}

static ArtBpath *
convert_glyph_code_to_begt1_path (Gt1PSContext *psc, Gt1String *glyph_code,
				  Gt1Dict *fontdict,
				  double *p_wx)
{
  BezState *bs;

  int i;
  int byte, byte1, byte2, byte3, byte4;
  double stack[256];
  int stack_ptr;
  double ps_stack[16];
  int ps_stack_ptr;
  Gt1String exe_stack[10];
  int ret_stack[10];
  int exe_stack_ptr;
  int subr;
  int val; /* needs to be int32! */

  /* stuff for flex */
  double flexbuf[6];
  int flexptr = -1;

  exe_stack_ptr = 0;
  exe_stack[0].start = gt1_alloc (glyph_code->size);
  exe_stack[0].size = glyph_code->size;
  charstring_decrypt (&exe_stack[0], glyph_code);

  bs = bs_new ();

  ps_stack_ptr = 0;
  stack_ptr = 0;
  for (i = 0; exe_stack_ptr || i < exe_stack[exe_stack_ptr].size; i++)
    {
      if (stack_ptr >= 240)
	goto error;
      byte = ((unsigned char *)exe_stack[exe_stack_ptr].start)[i];
      if (byte >= 32 && byte <= 246)
	stack[stack_ptr++] = byte - 139;
      else if (byte >= 247 && byte <= 250)
	{
	  byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  stack[stack_ptr++] = ((byte - 247) << 8) + byte1 + 108;
	}
      else if (byte >= 251 && byte <= 254)
	{
	  byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  stack[stack_ptr++] = -((byte - 251) << 8) - byte1 - 108;
	}
      else if (byte == 255)
	{
	  byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  byte2 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  byte3 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  byte4 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  val = (byte1 << 24) + (byte2 << 16) + (byte3 << 8) + byte4;
	  stack[stack_ptr++] = val;
	}
      else if (byte == 12)
	{
	  byte1 = ((unsigned char *)exe_stack[exe_stack_ptr].start)[++i];
	  if (byte1 == 6)
	    printf (" seac");
	  else if (byte1 == 7)
	    printf (" sbw");
	  else if (byte1 == 0)
	    {
	      /* dotsection */
	    }
	  else if (byte1 == 2)
	    {
	      /* hstem3 */
	      stack_ptr -= 6;
	    }
	  else if (byte1 == 1)
	    {
	      /* vstem3 */
	      stack_ptr -= 6;
	    }
	  else if (byte1 == 12)
	    {
	      /* div */
	      if (stack_ptr < 2)
		goto error;
	      if (stack[stack_ptr - 1] == 0)
		goto error;
	      stack[stack_ptr - 2] = stack[stack_ptr - 2] / stack[stack_ptr - 1];
	      stack_ptr--;
	    }
	  else if (byte1 == 16)
	    {
	      /* callothersubr */
	      int j;
	      int subr;
	      int n_args;

	      if (stack_ptr < 2)
		goto error;
	      subr = stack[--stack_ptr];
	      n_args = stack[--stack_ptr];
	      if (stack_ptr < n_args)
		goto error;
	      if (ps_stack_ptr + n_args > 16)
		goto error;
	      for (j = 0; j < n_args; j++)
		ps_stack[ps_stack_ptr++] = stack[--stack_ptr];
#ifdef VERBOSE
	      printf ("%d %d callothersubr\nPS:", n_args, subr);
	      for (j = 0; j < ps_stack_ptr; j++)
		printf (" %g", ps_stack[j]);
	      printf ("\nstack:");
	      for (j = 0; j < stack_ptr; j++)
		printf (" %g", stack[j]);
	      printf ("\n");
#endif
#undef VERBOSE
	      if (subr == 3)
		{
		  if (ps_stack_ptr < 1)
		    goto error;
		  ps_stack[ps_stack_ptr - 1] = 3;
		}
	      else if (subr == 0)
		{
		  if (ps_stack_ptr < 3)
		    goto error;
		  ps_stack_ptr--;
		}
	      else if (subr == 1)
		{
		  bs_do_moveto (bs);
		  flexptr = -2;
		}
	      else if (subr == 2)
		{
		  if (flexptr >= 0)
		    {
		      flexbuf[flexptr] = bs->x;
		      flexbuf[flexptr + 1] = bs->y;
		    }
		  flexptr += 2;
		  if (flexptr == 6)
		    {
		      bs_curveto (bs, flexbuf);
		      flexptr = 0;
		    }
		}
	    }
	  else if (byte1 == 17)
	    {
	      /* pop */
	      if (ps_stack_ptr == 0)
		goto error;
	      stack[stack_ptr++] = ps_stack[--ps_stack_ptr];
	    }
	  else if (byte1 == 33)
	    {
	      /* setcurrentpoint */
#ifdef VERBOSE
	      printf ("%g %g setcurrentpoint, bs is %g, %g\n",
		      stack[stack_ptr - 2], stack[stack_ptr - 1],
		      bs->x, bs->y);
#endif
	      bs->x = stack[stack_ptr - 2];
	      bs->y = stack[stack_ptr - 1];
	      stack_ptr -= 2;
	    }
	  else
	    printf (" esc%d", byte1);
	}
      else if (byte == 14)
	{
	  /* endchar */
	  /* nothing really to do here, except maybe some sanitychecking */
	}
      else if (byte == 13)
	{
	  /* hsbw */
	  bs_moveto (bs, stack[stack_ptr - 2], 0);
	  if (p_wx)
	    *p_wx = stack[stack_ptr - 1];
	  stack_ptr -= 2;
	}
      else if (byte == 9)
	{
	  /* closepath */
	  bs_closepath (bs);
	}
      else if (byte == 6)
	{
	  /* hlineto */
	  bs_rlineto (bs, stack[--stack_ptr], 0);
	}
      else if (byte == 22)
	{
	  /* hmoveto */
	  bs_rmoveto (bs, stack[--stack_ptr], 0);
	}
      else if (byte == 31)
	{
	  /* hvcurveto */
	  bs_rcurveto (bs, stack[stack_ptr - 4], 0,
		       stack[stack_ptr - 3], stack[stack_ptr - 2],
		       0, stack[stack_ptr - 1]);
	  stack_ptr -= 4;
	}
      else if (byte == 5)
	{
	  /* rlineto */
	  bs_rlineto (bs, stack[stack_ptr - 2], stack[stack_ptr - 1]);
	  stack_ptr -= 2;
	}
      else if (byte == 21)
	{
	  /* rmoveto */
	  bs_rmoveto (bs, stack[stack_ptr - 2], stack[stack_ptr - 1]);
	  stack_ptr -= 2;
	}
      else if (byte == 8)
	{
	  /* rrcurveto */
	  bs_rcurveto (bs, stack[stack_ptr - 6], stack[stack_ptr - 5],
		       stack[stack_ptr - 4], stack[stack_ptr - 3],
		       stack[stack_ptr - 2], stack[stack_ptr - 1]);
	  stack_ptr -= 6;
	}
      else if (byte == 30)
	{
	  /* vhcurveto */
	  bs_rcurveto (bs, 0, stack[stack_ptr - 4],
		       stack[stack_ptr - 3], stack[stack_ptr - 2],
		       stack[stack_ptr - 1], 0);
	  stack_ptr -= 4;
	}
      else if (byte == 7)
	{
	  /* vlineto */
	  bs_rlineto (bs, 0, stack[--stack_ptr]);
	}
      else if (byte == 4)
	{
	  /* vmoveto */
	  bs_rmoveto (bs, 0, stack[--stack_ptr]);
	}
      else if (byte == 1)
	{
	  /* hstem */
	  stack_ptr -= 2;
	}
      else if (byte == 3)
	{
	  /* vstem */
	  stack_ptr -= 2;
	}
      else if (byte == 10)
	{
	  /* callsubr */
	  subr = stack[--stack_ptr];
#ifdef VERBOSE
	  printf ("  /* call subr %d */\n", subr);
#endif
	  ret_stack[exe_stack_ptr] = i;
	  exe_stack_ptr++;
	  if (exe_stack_ptr == 10)
	    goto error;
	  get_subr_body (psc, &exe_stack[exe_stack_ptr], fontdict, subr);
	  i = -1;
	}
      else if (byte == 11)
	{
	  /* return */
#ifdef VERBOSE
	  printf ("  /* return */\n");
#endif
	  /* check exe_stack_ptr */
	  gt1_free (exe_stack[exe_stack_ptr].start);
	  exe_stack_ptr--;
	  i = ret_stack[exe_stack_ptr];
	}
      else
	printf (" com%d", byte);
    }
#ifdef VERBOSE
  printf ("\n");
#endif
  gt1_free (exe_stack[0].start);

  if (stack_ptr != 0)
    printf ("warning: stack_ptr = %d\n", stack_ptr);
  if (ps_stack_ptr != 0)
    printf ("warning: ps_stack_ptr = %d\n", ps_stack_ptr);

  return bs_end (bs);
 error:
  gt1_free (bs_end (bs));
  return NULL;
}

/* some functions for dealing with defined fonts after evaluation of the
   font program is complete. */

typedef struct _MunchedFontInfo MunchedFontInfo;
typedef struct _KernPair KernPair;

struct _Gt1LoadedFont {
  Gt1PSContext *psc;
  Font_Info *fi;
  MunchedFontInfo *mfi;
  Gt1Dict *fontdict;
#if 0
  Gt1NameId *encoding;
#else
	GHashTable *encoding;
#endif
  Gt1NameId id_charstrings;
  GHashTable *glyph_outlines;
};

/* The kern pair table is actually a hash table */
struct _MunchedFontInfo {
  int kern_pair_table_size;
  KernPair *kern_pair_table;
};

struct _KernPair {
  Gt1NameId name1; /* or -1 if empty */
  Gt1NameId name2;
  int xamt, yamt;
};

#define kern_pair_hash(n1,n2) ((n1) * 367 + (n2))

static MunchedFontInfo *
munch_font_info (Gt1PSContext *psc, Font_Info *fi)
{
  MunchedFontInfo *mfi;
  KernPair *table;
  int table_size;
  int i, j;
  Gt1NameId name1, name2;

  mfi = gt1_new (MunchedFontInfo, 1);

  table_size = fi->numOfPairs << 1;
  mfi->kern_pair_table_size = table_size;
  table = gt1_new (KernPair, table_size);
  mfi->kern_pair_table = table;
  for (i = 0; i < mfi->kern_pair_table_size; i++)
    table[i].name1 = -1;

  /* Transfer afm kern pair information into the hash table,
     taking care to intern the names as we go. */
  for (i = 0; i < fi->numOfPairs; i++)
    {
      name1 = gt1_name_context_intern (psc->nc, fi->pkd[i].name1);
      name2 = gt1_name_context_intern (psc->nc, fi->pkd[i].name2);
      for (j = kern_pair_hash (name1, name2);
	   table[j % table_size].name1 != -1;
	   j++);
      j = j % table_size;
      table[j].name1 = name1;
      table[j].name2 = name2;
      table[j].xamt = fi->pkd[i].xamt;
      table[j].yamt = fi->pkd[i].yamt;
    }

  return mfi;
}

static void
free_munched_font_info (MunchedFontInfo *mfi)
{
  gt1_free (mfi->kern_pair_table);
  gt1_free (mfi);
}


/* allocate a new filename, same as the old one, but with the extension */
static char *
replace_extension (const char *filename, const char *ext)
{
  int i;
  int size_fn, size_ext;
  char *new_fn;

  size_fn = strlen (filename);
  size_ext = strlen (ext);
  for (i = size_fn - 1; i >= 0; i--)
    if (filename[i] == '.' || filename[i] == '/')
      break;
  if (filename[i] != '.')
    i = size_fn;
  new_fn = gt1_new (char, i + size_ext + 1);
  memcpy (new_fn, filename, i);
  memcpy (new_fn + i, ext, size_ext);
  new_fn[i + size_ext] = '\0';
  return new_fn;
}

static guint
glyph_outline_hash (gconstpointer v)
{
	const Gt1GlyphOutline *go = v;

	return go->ch;
}

static gint
glyph_outline_equal (gconstpointer a, gconstpointer b)
{
	const Gt1GlyphOutline *ga = a;
	const Gt1GlyphOutline *gb = b;

	if (ga->ch == gb->ch)
		return TRUE;
	else
		return FALSE;
}

/* Find the bounding box of a bezier path. */
static void
art_drect_bpath (ArtDRect *bbox, const ArtBpath *bpath)
{
	/* This implementation is a hack. The real thing needs to go
	   into libart. */
	ArtVpath *vpath;

	vpath = art_bez_path_to_vec (bpath, 0.25);
	art_vpath_bbox_drect (vpath, bbox);
	art_free (vpath);
}

Gt1GlyphOutline *
gt1_glyph_outline_lookup (Gt1LoadedFont *font, int c)
{
	Gt1GlyphOutline o, *go;

	o.ch = c;
	if ((go = g_hash_table_lookup (font->glyph_outlines, &o)))
		return go;
	
	go = g_new (Gt1GlyphOutline, 1);

	go->ch = c;
	go->bpath = gt1_get_glyph_outline (font, c, &go->wx);
	if (!go->bpath)
		return NULL;

	art_drect_bpath (&go->bbox, go->bpath);

	g_hash_table_insert (font->glyph_outlines, go, go);

	return go;
}

static void
destroy_outline (gpointer key, gpointer value, gpointer user_data)
{
	Gt1GlyphOutline *o = key;

	if (o->bpath)
		art_free (o->bpath);
	o->bpath = NULL;
	g_free (o);
}

static void
glyph_outlines_destroy (Gt1LoadedFont *font)
{
	if (font->glyph_outlines)
		g_hash_table_foreach (font->glyph_outlines,
				      destroy_outline, NULL);
}

Gt1LoadedFont *
gt1_load_font (const char *filename, GHashTable *privencoding)
{
  Gt1LoadedFont *loaded_font;
  Gt1Dict *fontdict;
  FILE *f;
  char *afm_filename;
  FILE *afm_f;
  int status;

  char *pfb;
  int pfb_size, pfb_size_max;
  int bytes_read;
  Gt1TokenContext *tc;
  Gt1PSContext *psc;
  Gt1NameContext *nc;

  char *flat;

  GHashTable *encoding;
  int i;

  f = fopen (filename, "rb");
  if (f == NULL) return NULL;

  pfb_size = 0;
  pfb_size_max = 32768;
  pfb = gt1_new (char, pfb_size_max);
  while (1)
    {
      bytes_read = fread (pfb + pfb_size, 1, pfb_size_max - pfb_size, f);
      if (bytes_read == 0) break;
      pfb_size += bytes_read;
      gt1_double (pfb, char, pfb_size_max);
    }
  fclose (f);

  /*
  fwrite (pfb, 1, pfb_size, stdout);
  */

  /* this is a good place to do a "magic" computation on the input file. */
  if (pfb_size)
    {
      if (((unsigned char *)pfb)[0] == 128)
	flat = pfb_to_flat (pfb, pfb_size);
      else
	{
	  flat = gt1_new (char, pfb_size + 1);
	  memcpy (flat, pfb, pfb_size);
	  flat[pfb_size] = 0;
	}
    }
  else
    {
      flat = gt1_new (char, 1);
      *flat = 0;
    }
  gt1_free (pfb);

  /*
  printf ("%s", flat);
  */

  /*
  test_token (flat);
  */


  tc = tokenize_new (flat);
  gt1_free (flat);
  psc = eval_ps (tc);
  tokenize_free (tc);

  if (psc->fonts->n_entries != 1)
    {
      pscontext_free (psc);
      return NULL;
    }

  loaded_font = gt1_new (Gt1LoadedFont, 1);
  loaded_font->psc = psc;
  loaded_font->glyph_outlines = g_hash_table_new (glyph_outline_hash,
						  glyph_outline_equal);
  
  fontdict = psc->fonts->entries[0].val.val.dict_val;
  loaded_font->fontdict = fontdict;

  loaded_font->fi = NULL;
  loaded_font->mfi = NULL;
  afm_filename = replace_extension (filename, ".afm");
  afm_f = fopen (afm_filename, "rb");

  if (afm_f != NULL)
    {
      status = parseFile (afm_f, &loaded_font->fi, P_GALL);
      fclose (afm_f);

      loaded_font->mfi = munch_font_info (psc, loaded_font->fi);
    }
  gt1_free (afm_filename);

  encoding = g_hash_table_new (NULL, NULL);

  nc = psc->nc;

  for (i = 0; i < nc->table_size; i++) {
	  Gt1NameContextHashEntry *nche;
	  gint unival;

	  nche = nc->table + i;

	  if (nche->name) {
		  unival = gp_unicode_from_ps (nche->name);
		  if (unival < 1) unival = gp_unicode_from_dingbats (nche->name);
		  if (unival < 1) {
			  if (privencoding) {
				  unival = GPOINTER_TO_INT (g_hash_table_lookup (privencoding, nche->name));
			  }
		  }
		  if (unival > 0) {
			  const GSList * l;
			  l = gp_multi_from_ps (nche->name);
			  if (l) {
				  while (l) {
					  g_hash_table_insert (encoding, l->data, GINT_TO_POINTER (nche->Gt1NameId));
					  l = l->next;
				  }
			  } else {
				  g_hash_table_insert (encoding, GINT_TO_POINTER (unival), GINT_TO_POINTER (nche->Gt1NameId));
			  }
		  }
	  }
  }

  loaded_font->encoding = encoding;
  loaded_font->id_charstrings = gt1_name_context_intern (psc->nc, "CharStrings");

  return loaded_font;
}

void
gt1_unload_font (Gt1LoadedFont *font)
{
  pscontext_free (font->psc);
  if (font->mfi) free_munched_font_info (font->mfi);
  if (font->fi) parseFileFree (font->fi);

  glyph_outlines_destroy (font);
  g_hash_table_destroy (font->glyph_outlines);

  g_hash_table_destroy (font->encoding);
  gt1_free (font);
}


#if 1
ArtBpath *
gt1_get_glyph_outline (Gt1LoadedFont *font, int glyphnum, double *p_wx)
{
  Gt1NameId glyphname;
  Gt1Value *charstringsval, *glyphcodeval;
  Gt1Dict *charstrings;
  Gt1String *glyphcode;

  /* fixme: */
  glyphname = GPOINTER_TO_INT (g_hash_table_lookup (font->encoding, GINT_TO_POINTER (glyphnum)));
  if (!glyphname) return NULL;

  charstringsval = gt1_dict_lookup (font->fontdict, font->id_charstrings);
  charstrings = charstringsval->val.dict_val;
  glyphcodeval = gt1_dict_lookup (charstrings, glyphname);
  if (glyphcodeval)
    {
      glyphcode = &glyphcodeval->val.str_val;
#ifdef VERBOSE
      /* note: there is an #undef VERBOSE above, so this is probably
	 going to be ineffective. */
      print_glyph_code (glyphcode);
#endif
      return convert_glyph_code_to_begt1_path (font->psc,
					       glyphcode, font->fontdict, p_wx);
    }
  else
    return NULL;
}
#else
#define CP1 22.4
#define CP2 (100-CP1)

ArtBpath shape[] = {

  { MOVETO, 0, 0, 0, 0, 0, -50 },
  { CURVETO, 0, -CP2, CP1, -100, 50, -100 },
  { CURVETO, CP2, -100, 100, -CP2, 100, -50 },
  { CURVETO, 100, -CP1, CP2, 0, 50, 0 },
  { CURVETO, CP1, 0, 0, -CP1, 0, -50 },
  { END, 0, 0, 0, 0, 0, 0 }
};

ArtBpath *
gt1_get_glyph_outline (Gt1LoadedFont *font, int glyphnum, double *p_wx)
{
  ArtBpath *bez;
  int i;

  bez = gt1_new (ArtBpath, 6);
  for (i = 0; i < 6; i++)
  bez[i] = shape[i];
  *p_wx = 120;
  return bez;
}
#endif

/* get xamt of kern pair */
double
gt1_get_kern_pair (Gt1LoadedFont *font, int glyph1, int glyph2)
{
  Gt1NameId name1, name2;
  int i, idx;
  KernPair *table;
  int table_size;

  if (font == NULL)
    return 0;
  name1 = GPOINTER_TO_INT (g_hash_table_lookup (font->encoding, GINT_TO_POINTER (glyph1)));
  name2 = GPOINTER_TO_INT (g_hash_table_lookup (font->encoding, GINT_TO_POINTER (glyph2)));
  table_size = font->mfi->kern_pair_table_size;
  table = font->mfi->kern_pair_table;
  for (i = kern_pair_hash (name1, name2); idx = i % table_size,
	 table[idx].name1 != -1;
       i++)
    if (table[idx].name1 == name1 && table[idx].name2 == name2)
      return table[idx].xamt;
  return 0;
}

char *
gt1_get_font_name (Gt1LoadedFont *font)
{
  return font->fi->gfi->fontName;
}

#ifdef MAIN
int
main (int argc, char **argv)
{
  char *pfb;
  int pfb_size, pfb_size_max;
  int bytes_read;
  Gt1TokenContext *tc;
  Gt1PSContext *psc;

  char *flat;

  pfb_size = 0;
  pfb_size_max = 32768;
  pfb = gt1_new (char, pfb_size_max);
  while (1)
    {
      bytes_read = fread (pfb + pfb_size, 1, pfb_size_max - pfb_size, stdin);
      if (bytes_read == 0) break;
      pfb_size += bytes_read;
      gt1_double (pfb, char, pfb_size_max);
    }

  /*
  fwrite (pfb, 1, pfb_size, stdout);
  */

  /* this is a good place to do a "magic" computation on the input file. */

  if (pfb_size)
    {
      if (((unsigned char *)pfb)[0] == 128)
	flat = pfb_to_flat (pfb, pfb_size);
      else
	{
	  flat = gt1_new (char, pfb_size + 1);
	  memcpy (flat, pfb, pfb_size);
	  flat[pfb_size] = 0;
	}
    }

  /*
  printf ("%s", flat);
  */

  /*
  test_token (flat);
  */

  tc = tokenize_new (flat);
  psc = eval_ps (tc);

  print_defined_fonts (psc);

  return 0;
}
#endif
