/* $Id: psread.c 4532 2015-12-22 13:18:53Z mskala $ */
/* Copyright (C) 2000-2012  George Williams
 * Copyright (C) 2015  Matthew Skala
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "fontanvil.h"
#include <math.h>
#include <ustring.h>
#include <utype.h>
#include "psfont.h"
#include "sd.h"
#include "views.h"		/* For CharViewBase */
#ifdef HAVE_IEEEFP_H
#   include <ieeefp.h>		/* Solaris defines isnan in ieeefp rather than math.h */
#endif

typedef struct _io {
   char *macro, *start;
   AFILE *ps, *fog;
   char fogbuf[60];
   int backedup, cnt, isloop, isstopped, fogns;
   struct _io *prev;
} _IO;

typedef struct io {
   struct _io *top;
   int endedstopped;
   int advance_width;		/* Can be set from a PS comment by MF2PT1 */
} IO;

#define GARBAGE_MAX	64
struct garbage {
   int cnt;
   struct garbage *next;
   struct pskeyval *entries[GARBAGE_MAX];
   int16_t cnts[GARBAGE_MAX];
};

static void AddTok(GrowBuf *gb,char *buf,int islit) {

   if (islit)
      GrowBufferAdd(gb, '/');
   GrowBufferAddStr(gb, buf);
   GrowBufferAdd(gb, ' ');
}

static struct pskeyval *lookup(struct pskeydict *dict,char *tokbuf) {
   int i;

   for (i=0; i<dict->cnt; ++i)
      if (strcmp(dict->entries[i].key, tokbuf)==0)
	 return (&dict->entries[i]);

   return (NULL);
}

static void dictfree(struct pskeydict *dict) {
   int i;

   for (i=0; i<dict->cnt; ++i) {
      if (dict->entries[i].type==ps_string
	  || dict->entries[i].type==ps_instr
	  || dict->entries[i].type==ps_lit)
	 free(dict->entries[i].u.str);
      else if (dict->entries[i].type==ps_array
	       || dict->entries[i].type==ps_dict)
	 dictfree(&dict->entries[i].u.dict);
   }
}

static void garbagefree(struct garbage *all) {
   struct garbage *junk, *next;
   int i, j;

   for (junk=all; junk != NULL; junk=next) {
      next=junk->next;
      for (j=0; j<junk->cnt; ++j) {
	 for (i=0; i<junk->cnts[j]; ++i) {
	    if (junk->entries[j][i].type==ps_string
		|| junk->entries[j][i].type==ps_instr
		|| junk->entries[j][i].type==ps_lit)
	       free(junk->entries[j][i].u.str);
	 }
	 free(junk->entries[j]);
      }
      if (junk != all)
	 chunkfree(junk, sizeof(struct garbage));
   }
}

/**************************** PostScript Importer *****************************/
/* It's really dumb. It ignores almost everything except linetos and curvetos */
/*  anything else, function calls, ... is thrown out, if this breaks a lineto */
/*  or curveto or moveto (if there aren't enough things on the stack) then we */
/*  ignore that too */

enum pstoks { pt_eof=-1, pt_moveto, pt_rmoveto, pt_curveto, pt_rcurveto,
   pt_lineto, pt_rlineto, pt_arc, pt_arcn, pt_arct, pt_arcto,
   pt_newpath, pt_closepath, pt_dup, pt_pop, pt_index,
   pt_exch, pt_roll, pt_clear, pt_copy, pt_count,
   pt_setcachedevice, pt_setcharwidth,
   pt_translate, pt_scale, pt_rotate, pt_concat, pt_end, pt_exec,
   pt_add, pt_sub, pt_mul, pt_div, pt_idiv, pt_mod, pt_neg,
   pt_abs, pt_round, pt_ceiling, pt_floor, pt_truncate, pt_max, pt_min,
   pt_ne, pt_eq, pt_gt, pt_ge, pt_lt, pt_le, pt_and, pt_or, pt_xor, pt_not,
   pt_exp, pt_sqrt, pt_ln, pt_log, pt_atan, pt_sin, pt_cos,
   pt_true, pt_false,
   pt_if, pt_ifelse, pt_for, pt_loop, pt_repeat, pt_exit,
   pt_stopped, pt_stop,
   pt_def, pt_bind, pt_load,
   pt_setlinecap, pt_setlinejoin, pt_setlinewidth, pt_setdash,
   pt_currentlinecap, pt_currentlinejoin, pt_currentlinewidth, pt_currentdash,
   pt_setgray, pt_currentgray, pt_sethsbcolor, pt_currenthsbcolor,
   pt_setrgbcolor, pt_currentrgbcolor, pt_setcmykcolor, pt_currentcmykcolor,
   pt_currentpoint,
   pt_fill, pt_stroke, pt_clip,

   pt_imagemask,

   pt_transform, pt_itransform, pt_dtransform, pt_idtransform,

   /* things we sort of pretend to do, but actually do something wrong */
   pt_gsave, pt_grestore, pt_save, pt_restore, pt_currentmatrix, pt_setmatrix,
   pt_null,

   pt_currentflat, pt_setflat,
   pt_currentglobal, pt_setglobal,
   pt_currentmiterlimit, pt_setmiterlimit,
   pt_currentobjectformat, pt_setobjectformat,
   pt_currentoverprint, pt_setoverprint,
   pt_currentpacking, pt_setpacking,
   pt_currentshared,
   pt_currentsmoothness, pt_setsmoothness,
   pt_currentstrokeadjust, pt_setstrokeadjust,

   pt_mark, pt_counttomark, pt_cleartomark, pt_array, pt_aload, pt_astore,
   pt_print, pt_cvi, pt_cvlit, pt_cvn, pt_cvr, pt_cvrs, pt_cvs, pt_cvx,
      pt_stringop,

   pt_opencurly, pt_closecurly, pt_openarray, pt_closearray, pt_string,
   pt_number, pt_unknown, pt_namelit, pt_output, pt_outputd,

   pt_showpage
};

static char *toknames[]={ "moveto","rmoveto","curveto","rcurveto",
   "lineto", "rlineto", "arc", "arcn", "arct", "arcto",
   "newpath", "closepath", "dup", "pop", "index",
   "exch", "roll", "clear", "copy", "count",
   "setcachedevice", "setcharwidth",
   "translate", "scale", "rotate", "concat", "end", "exec",
   "add", "sub", "mul", "div", "idiv", "mod", "neg",
   "abs", "round", "ceiling", "floor", "truncate", "max", "min",
   "ne", "eq", "gt", "ge", "lt", "le", "and", "or", "xor", "not",
   "exp", "sqrt", "ln", "log", "atan", "sin", "cos",
   "true", "false",
   "if", "ifelse", "for", "loop", "repeat", "exit",
   "stopped", "stop",
   "def", "bind", "load",
   "setlinecap", "setlinejoin", "setlinewidth", "setdash",
   "currentlinecap", "currentlinejoin", "currentlinewidth", "currentdash",
   "setgray", "currentgray", "sethsbcolor", "currenthsbcolor",
   "setrgbcolor", "currentrgbcolor", "setcmykcolor", "currentcmykcolor",
   "currentpoint",
   "fill", "stroke", "clip",

   "imagemask",

   "transform", "itransform", "dtransform", "idtransform",

   "gsave", "grestore", "save", "restore", "currentmatrix", "setmatrix",
   "null",

   "currentflat", "setflat",
   "currentglobal", "setglobal",
   "currentmiterlimit", "setmiterlimit",
   "currentobjectformat", "setobjectformat",
   "currentoverprint", "setoverprint",
   "currentpacking", "setpacking",
   "currentshared",
   "currentsmoothness", "setsmoothness",
   "currentstrokeadjust", "setstrokeadjust",

   "mark", "counttomark", "cleartomark", "array", "aload", "astore",
   "print", "cvi", "cvlit", "cvn", "cvr", "cvrs", "cvs", "cvx", "string",

   "opencurly", "closecurly", "openarray", "closearray", "string",
   "number", "unknown", "namelit", "=", "==",

   "showpage",

   NULL
};

/* length (of string)
    fill eofill stroke
    gsave grestore
*/

static int getfoghex(_IO *io) {
   int ch, val;

   while (isspace(ch=agetc(io->fog)));
   if (isdigit(ch))
      val=ch-'0';
   else if (ch >= 'A' && ch <= 'F')
      val=ch-'A'+10;
   else if (ch >= 'a' && ch <= 'f')
      val=ch-'a'+10;
   else
      return (EOF);

   val <<= 4;
   while (isspace(ch=agetc(io->fog)));
   if (isdigit(ch))
      val |= ch-'0';
   else if (ch >= 'A' && ch <= 'F')
      val |= ch-'A'+10;
   else if (ch >= 'a' && ch <= 'f')
      val |= ch-'a'+10;
   else
      return (EOF);

   return (val);
}

static int nextch(IO *wrapper) {
   int ch;
   _IO *io=wrapper->top;

/* This works for fog 4.1. Fonts generated by 2.4 seem to use a different */
/*  vector, and a different number parsing scheme */
   static char *foguvec[]={ "moveto ","rlineto ","rrcurveto "," "," ",
      "Cache ", "10 div setlinewidth ", "ShowInt ", " ", " ", " ", " ",
      "FillStroke ", " ", " ", "SetWid ", "100 mul add ", "togNS_ ",
      " ", "closepath ", " ", "SG "
   };

   while (io != NULL) {
      if (io->backedup != EOF) {
	 ch=io->backedup;
	 io->backedup=EOF;
	 return (ch);
      } else if (io->ps != NULL) {
	 if ((ch=agetc(io->ps)) != EOF)
	    return (ch);
      } else if (io->fog != NULL) {
	 if (io->macro != NULL && *io->macro != '\0')
	    return (*(io->macro++));
	 ch=getfoghex(io);
	 if (ch >= 233) {
	    io->macro=foguvec[ch-233];
	    return (*(io->macro++));
	 } else if (ch != EOF && ch<200) {
	    sprintf(io->fogbuf, "%d ", ch-100);
	    io->macro=io->fogbuf;
	    return (*(io->macro++));
	 } else if (ch != EOF) {
	    sprintf(io->fogbuf, "%d %s ", ch-233+17, io->fogns
		   ?"2 exch exp 3 1 roll 100 mul add mul":"100 mul add");
	    io->macro=io->fogbuf;
	    return (*(io->macro++));
	 }
      } else {
	 if ((ch=*(io->macro++)) != '\0')
	    return (ch);
	 if (--io->cnt>0) {
	    io->macro=io->start;
	    return (nextch(wrapper));
	 }
      }
      wrapper->top=io->prev;
      if (io->isstopped)
	 wrapper->endedstopped=true;
      free(io->start);
      free(io);
      io=wrapper->top;
   }
   return (EOF);
}

static void unnextch(int ch,IO *wrapper) {
   if (ch==EOF)
      return;
   if (wrapper->top==NULL)
      ErrorMsg(2,"Can't back up with nothing on stack\n");
   else if (wrapper->top->backedup != EOF)
      ErrorMsg(2,"Attempt to back up twice\n");
   else if (wrapper->top->ps != NULL)
      aungetc(ch, wrapper->top->ps);
   else
      wrapper->top->backedup=ch;
}

static void pushio(IO *wrapper,AFILE *ps,char *macro,int cnt) {
   _IO *io=calloc(1, sizeof(_IO));

   io->prev=wrapper->top;
   io->ps=ps;
   io->macro=io->start=fastrdup(macro);
   io->backedup=EOF;
   if (cnt==-1) {
      io->cnt=1;
      io->isstopped=true;
   } else if (cnt==0) {
      io->cnt=1;
      io->isloop=false;
   } else {
      io->cnt=cnt;
      io->isloop=true;
   }
   wrapper->top=io;
}

static void pushfogio(IO *wrapper,AFILE *fog) {
   _IO *io=calloc(1, sizeof(_IO));

   io->prev=wrapper->top;
   io->fog=fog;
   io->backedup=EOF;
   io->cnt=1;
   io->isloop=false;
   wrapper->top=io;
}

static void ioescapeloop(IO *wrapper) {
   _IO *io=wrapper->top, *iop;
   int wasloop;

   while (io->prev != NULL && !io->isstopped) {
      iop=io->prev;
      wasloop=io->isloop;
      free(io->start);
      free(io);
      if (wasloop) {
	 wrapper->top=iop;
	 return;
      }
      io=iop;
   }

/* GT: This is part of the PostScript language. "exit" should not be translated */
/* GT: as it is a PostScript keyword. (FF contains a small PostScript interpreter */
/* GT: so it can understand some PostScript fonts, and can generate errors when */
/* GT: handed bad PostScript). */
   ErrorMsg(2,"Use of \"exit\" when not in a loop\n");
   wrapper->top=io;
}

static int ioescapestopped(IO *wrapper,struct psstack *stack,int sp,
			   const size_t bsize) {
   _IO *io=wrapper->top, *iop;
   int wasstopped;

   while (io->prev != NULL) {
      iop=io->prev;
      wasstopped=io->isstopped;
      free(io->start);
      free(io);
      if (wasstopped) {
	 wrapper->top=iop;
	 if (sp<(int) bsize) {
	    stack[sp].type=ps_bool;
	    stack[sp++].u.tf=true;
	 }
	 return (sp);
      }
      io=iop;
   }

/* GT: This is part of the PostScript language. Neither "stop" nor "stopped" */
/* GT: should be translated as both are PostScript keywords. */
   ErrorMsg(2,"Use of \"stop\" when not in a stopped\n");
   wrapper->top=io;
   return (sp);
}

static int endedstopped(IO *wrapper) {
   if (wrapper->endedstopped) {
      wrapper->endedstopped=false;
      return (true);
   }
   return (false);
}

static int CheckCodePointsComment(IO *wrapper) {
   /* Check to see if this encoding includes the special comment we use */
   /*  to indicate that the encoding is based on unicode code points rather */
   /*  than glyph names */
   char commentbuffer[128], *pt;
   int ch;

   /* Eat whitespace and comments. Comments last to eol (or formfeed) */
   while (isspace(ch=nextch(wrapper)));
   if (ch != '%') {
      unnextch(ch, wrapper);
      return (false);
   }

   pt=commentbuffer;
   while ((ch=nextch(wrapper)) != EOF && ch != '\r' && ch != '\n'
	  && ch != '\f') {
      if (pt-commentbuffer<sizeof(commentbuffer)-1)
	 *pt++=ch;
   }
   *pt='\0';
   if (strcmp(commentbuffer, " Use codepoints.")==0)
      return (true);

   return (false);
}

static int nextpstoken(IO *wrapper,double *val,char *tokbuf,int tbsize) {
   int ch, r, i;
   char *pt, *end;
   float mf2pt_advance_width;

   pt=tokbuf;
   end=pt+tbsize-1;

   /* Eat whitespace and comments. Comments last to eol (or formfeed) */
   while (1) {
      while (isspace(ch=nextch(wrapper)));
      if (ch != '%')
	 break;
      while ((ch=nextch(wrapper)) != EOF && ch != '\r' && ch != '\n'
	     && ch != '\f')
	 if (pt<end)
	    *pt++=ch;
      *pt='\0';
      /* Some comments have meanings (that we care about) */
      if (sscanf(tokbuf, " MF2PT1: bbox %*g %*g %g %*g", &mf2pt_advance_width)
	 ==1)
	 wrapper->advance_width=mf2pt_advance_width;
      else
	 if (sscanf
	     (tokbuf, " MF2PT1: glyph_dimensions %*g %*g %g %*g",
	      &mf2pt_advance_width)==1)
	 wrapper->advance_width=mf2pt_advance_width;
      pt=tokbuf;
   }

   if (ch==EOF)
      return (pt_eof);

   pt=tokbuf;
   end=pt+tbsize-1;
   *pt++=ch;
   *pt='\0';

   if (ch=='(') {
      int nest=1, quote=0;

      while ((ch=nextch(wrapper)) != EOF) {
	 if (pt<end)
	    *pt++=ch;
	 if (quote)
	    quote=0;
	 else if (ch=='(')
	    ++nest;
	 else if (ch==')') {
	    if (--nest==0)
	       break;
	 } else if (ch=='\\')
	    quote=1;
      }
      *pt='\0';
      return (pt_string);
   } else if (ch=='<') {
      ch=nextch(wrapper);
      if (pt<end)
	 *pt++=ch;
      if (ch=='>')
	 /* Done */ ;
      else if (ch != '~') {
	 while ((ch=nextch(wrapper)) != EOF && ch != '>')
	    if (pt<end)
	       *pt++=ch;
      } else {
	 int twiddle=0;

	 while ((ch=nextch(wrapper)) != EOF) {
	    if (pt<end)
	       *pt++=ch;
	    if (ch=='~')
	       twiddle=1;
	    else if (twiddle && ch=='>')
	       break;
	    else
	       twiddle=0;
	 }
      }
      *pt='\0';
      return (pt_string);
   } else if (ch==')' || ch=='>' || ch=='[' || ch==']' || ch=='{'
	      || ch=='}') {
      if (ch=='{')
	 return (pt_opencurly);
      else if (ch=='}')
	 return (pt_closecurly);
      if (ch=='[')
	 return (pt_openarray);
      else if (ch==']')
	 return (pt_closearray);

      return (pt_unknown);	/* single character token */
   } else if (ch=='/') {
      pt=tokbuf;
      while ((ch=nextch(wrapper)) != EOF && !isspace(ch) && ch != '%' &&
	     ch != '(' && ch != ')' && ch != '<' && ch != '>' && ch != '['
	     && ch != ']' && ch != '{' && ch != '}' && ch != '/')
	 if (pt<tokbuf+tbsize-2)
	    *pt++=ch;
      *pt='\0';
      unnextch(ch, wrapper);
      return (pt_namelit);	/* name literal */
   } else {
      while ((ch=nextch(wrapper)) != EOF && !isspace(ch) && ch != '%' &&
	     ch != '(' && ch != ')' && ch != '<' && ch != '>' && ch != '['
	     && ch != ']' && ch != '{' && ch != '}' && ch != '/') {
	 if (pt<tokbuf+tbsize-2)
	    *pt++=ch;
      }
      *pt='\0';
      unnextch(ch, wrapper);
      r=strtol(tokbuf, &end, 10);
      pt=end;
      if (*pt=='\0') {	/* It's a normal integer */
	 *val=r;
	 return (pt_number);
      } else if (*pt=='#') {
	 r=strtol(pt+1, &end, r);
	 if (*end=='\0') {	/* It's a radix integer */
	    *val=r;
	    return (pt_number);
	 }
      } else {
	 *val=strtod(tokbuf, &end);
	 if (!isfinite(*val)) {
/* GT: NaN is a concept in IEEE floating point which means "Not a Number" */
/* GT: it is used to represent errors like 0/0 or sqrt(-1). */
	    ErrorMsg(2,"Bad number, infinity or nan: %s\n", tokbuf);
	    *val=0;
	 }
	 if (*end=='\0')	/* It's a real */
	    return (pt_number);
      }
      /* It's not a number */
      for (i=0; toknames[i] != NULL; ++i)
	 if (strcmp(tokbuf, toknames[i])==0)
	    return (i);

      return (pt_unknown);
   }
}

static void Transform(BasePoint *to,DBasePoint *from,double trans[6]) {
   to->x=trans[0] * from->x+trans[2] * from->y+trans[4];
   to->y=trans[1] * from->x+trans[3] * from->y+trans[5];
}

void MatMultiply(double m1[6], double m2[6], double to[6]) {
   double trans[6];

   trans[0]=m1[0] * m2[0]+m1[1] * m2[2];
   trans[1]=m1[0] * m2[1]+m1[1] * m2[3];
   trans[2]=m1[2] * m2[0]+m1[3] * m2[2];
   trans[3]=m1[2] * m2[1]+m1[3] * m2[3];
   trans[4]=m1[4] * m2[0]+m1[5] * m2[2]+m2[4];
   trans[5]=m1[4] * m2[1]+m1[5] * m2[3]+m2[5];
   memcpy(to, trans, sizeof(trans));
}

void MatInverse(double into[6], double orig[6]) {
   double det=orig[0] * orig[3]-orig[1] * orig[2];

   if (det==0) {
      ErrorMsg(2,"Attempt to invert a singular matrix\n");
      memset(into, 0, sizeof(*into));
   } else {
      into[0]=orig[3]/det;
      into[1]=-orig[1]/det;
      into[2]=-orig[2]/det;
      into[3]=orig[0]/det;
      into[4]=-orig[4] * into[0]-orig[5] * into[2];
      into[5]=-orig[4] * into[1]-orig[5] * into[3];
   }
}

int MatIsIdentity(double transform[6]) {
   return (transform[0]==1 && transform[3]==1 && transform[1]==0
	   && transform[2]==0 && transform[4]==0 && transform[5]==0);
}

static void ECCategorizePoints(EntityChar *ec) {
   Entity *ent;

   for (ent=ec->splines; ent != NULL; ent=ent->next)
      if (ent->type==et_splines) {
	 SPLCategorizePoints(ent->u.splines.splines);
	 SPLCategorizePoints(ent->clippath);
      }
}

static int AddEntry(struct pskeydict *dict,struct psstack *stack,int sp) {
   int i;

   if (dict->cnt >= dict->max) {
      if (dict->cnt==0) {
	 dict->max=30;
	 dict->entries=malloc(dict->max * sizeof(struct pskeyval));
      } else {
	 dict->max += 30;
	 dict->entries =
	    realloc(dict->entries, dict->max * sizeof(struct pskeyval));
      }
   }
   if (sp<2)
      return (sp);
   if (stack[sp-2].type != ps_string && stack[sp-2].type != ps_lit) {
/* GT: Here "def" is a PostScript keyword, (meaning define). */
/* GT: This "def" should not be translated as it is part of the PostScript language. */
      ErrorMsg(2,"Key for a def must be a string or name literal\n");
      return (sp-2);
   }
   for (i=0; i<dict->cnt; ++i)
      if (strcmp(dict->entries[i].key, stack[sp-2].u.str)==0)
	 break;
   if (i != dict->cnt) {
      free(stack[sp-2].u.str);
      if (dict->entries[i].type==ps_string
	  || dict->entries[i].type==ps_instr
	  || dict->entries[i].type==ps_lit)
	 free(dict->entries[i].u.str);
   } else {
      memset(&dict->entries[i], '\0', sizeof(struct pskeyval));
      dict->entries[i].key=stack[sp-2].u.str;
      ++dict->cnt;
   }
   dict->entries[i].type=stack[sp-1].type;
   dict->entries[i].u=stack[sp-1].u;
   return (sp-2);
}

static int forgetstack(struct psstack *stack,int forgets,int sp) {
   /* forget the bottom most "forgets" entries on the stack */
   /* we presume they are garbage that has accumulated because we */
   /*  don't understand all of PS */
   int i;

   for (i=0; i<forgets; ++i) {
      if (stack[i].type==ps_string || stack[i].type==ps_instr ||
	  stack[i].type==ps_lit)
	 free(stack[i].u.str);
      else if (stack[i].type==ps_array || stack[i].type==ps_dict)
	 dictfree(&stack[i].u.dict);
   }
   for (i=forgets; i<sp; ++i)
      stack[i-forgets]=stack[i];
   return (sp-forgets);
}

static int rollstack(struct psstack *stack,int sp) {
   int n, j, i;
   struct psstack *temp;

   if (sp>1) {
      n=stack[sp-2].u.val;
      j=stack[sp-1].u.val;
      sp -= 2;
      if (sp >= n && n>0) {
	 j %= n;
	 if (j<0)
	    j += n;
	 temp=malloc(n * sizeof(struct psstack));
	 for (i=0; i<n; ++i)
	    temp[i]=stack[sp-n+i];
	 for (i=0; i<n; ++i)
	    stack[sp-n+(i+j)%n]=temp[i];
	 free(temp);
      }
   }
   return (sp);
}

static void CheckMakeB(BasePoint *test,BasePoint *good) {
   if (!isfinite(test->x) || test->x>100000 || test->x<-100000) {
      ErrorMsg(2,"Value out of bounds in spline.\n");
      if (good != NULL)
	 test->x=good->x;
      else
	 test->x=0;
   }
   if (!isfinite(test->y) || test->y>100000 || test->y<-100000) {
      ErrorMsg(2,"Value out of bounds in spline.\n");
      if (good != NULL)
	 test->y=good->y;
      else
	 test->y=0;
   }
}

static void CheckMake(SplinePoint *from,SplinePoint *to) {
   CheckMakeB(&from->me, NULL);
   CheckMakeB(&from->nextcp, &from->me);
   CheckMakeB(&to->prevcp, &from->nextcp);
   CheckMakeB(&to->me, &to->prevcp);
}

static void circlearcto(double a1,double a2,double cx,double cy,double r,
			SplineSet * cur, double * transform) {
   SplinePoint *pt;
   DBasePoint temp, base, cp;
   double cplen;
   int sign=1;
   double s1, s2, c1, c2;

   if (a1==a2)
      return;

   cplen=(a2-a1)/90*r * .552;
   a1 *= M_PI/180;
   a2 *= M_PI/180;
   s1=sin(a1);
   s2=sin(a2);
   c1=cos(a1);
   c2=cos(a2);
   temp.x=cx+r * c2;
   temp.y=cy+r * s2;
   base.x=cx+r * c1;
   base.y=cy+r * s1;
   pt=chunkalloc(sizeof(SplinePoint));
   Transform(&pt->me, &temp, transform);
   cp.x=temp.x-cplen * s2;
   cp.y=temp.y+cplen * c2;
   if ((cp.x-base.x)*(cp.x-base.x)+(cp.y-base.y)*(cp.y-base.y) >
       (temp.x-base.x)*(temp.x-base.x)+(temp.y-base.y)*(temp.y -
								    base.y)) {
      sign=-1;
      cp.x=temp.x+cplen * s2;
      cp.y=temp.y-cplen * c2;
   }
   Transform(&pt->prevcp, &cp, transform);
   pt->nonextcp=true;
   cp.x=base.x+sign * cplen * s1;
   cp.y=base.y-sign * cplen * c1;
   Transform(&cur->last->nextcp, &cp, transform);
   cur->last->nonextcp=false;
   CheckMake(cur->last, pt);
   SplineMake3(cur->last, pt);
   cur->last=pt;
}

static void circlearcsto(double a1,double a2,double cx,double cy,double r,
			 SplineSet * cur, double * transform, int clockwise) {
   int a;
   double last;

   while (a1<0) {
      a1 += 360;
      a2 += 360;
   }
   while (a2-a1 <= -360)
      a2 += 360;
   while (a1>360) {
      a1 -= 360;
      a2 -= 360;
   }
   while (a2-a1>360)
      a2 -= 360;
   if (!clockwise) {
      if (a1>a2)
	 a2 += 360;
      last=a1;
      for (a=((int) (a1+90)/90)*90; a<a2; a += 90) {
	 circlearcto(last, a, cx, cy, r, cur, transform);
	 last=a;
      }
      circlearcto(last, a2, cx, cy, r, cur, transform);
   } else {
      if (a2>a1)
	 a1 += 360;
      last=a1;
      for (a=((int) (a1-90)/90)*90+90; a>a2; a -= 90) {
	 circlearcto(last, a, cx, cy, r, cur, transform);
	 last=a;
      }
      circlearcto(last, a2, cx, cy, r, cur, transform);
   }
}

static void collectgarbage(struct garbage *tofrees,struct pskeydict *to) {
   struct garbage *into;

   /* Garbage collection pointers */
   into=tofrees;
   if (tofrees->cnt >= GARBAGE_MAX && tofrees->next != NULL)
      into=tofrees->next;
   if (into->cnt >= GARBAGE_MAX) {
      into=chunkalloc(sizeof(struct garbage));
      into->next=tofrees->next;
      tofrees->next=into;
   }
   into->cnts[into->cnt]=to->cnt;
   into->entries[into->cnt++]=to->entries;
}

static void copyarray(struct pskeydict *to,struct pskeydict *from,
		      struct garbage *tofrees) {
   int i;
   struct pskeyval *oldent=from->entries;

   *to=*from;
   to->entries=calloc(to->cnt, sizeof(struct pskeyval));
   for (i=0; i<to->cnt; ++i) {
      to->entries[i]=oldent[i];
      if (to->entries[i].type==ps_string || to->entries[i].type==ps_instr
	  || to->entries[i].type==ps_lit)
	 to->entries[i].u.str=fastrdup(to->entries[i].u.str);
      else if (to->entries[i].type==ps_array
	       || to->entries[i].type==ps_dict)
	 copyarray(&to->entries[i].u.dict, &oldent[i].u.dict, tofrees);
   }
   collectgarbage(tofrees, to);
}

static int aload(int sp,struct psstack *stack,int stacktop,
		 struct garbage *tofrees) {
   int i;

   if (sp >= 1 && stack[sp-1].type==ps_array) {
      struct pskeydict dict;

      --sp;
      dict=stack[sp].u.dict;
      for (i=0; i<dict.cnt; ++i) {
	 if (sp<stacktop) {
	    stack[sp].type=dict.entries[i].type;
	    stack[sp].u=dict.entries[i].u;
	    if (stack[sp].type==ps_string || stack[sp].type==ps_instr ||
		stack[sp].type==ps_lit)
	       stack[sp].u.str=fastrdup(stack[sp].u.str);
/* The following is incorrect behavior, but as I don't do garbage collection */
/*  and I'm not going to implement reference counts, this will work in most cases */
	    else if (stack[sp].type==ps_array)
	       copyarray(&stack[sp].u.dict, &stack[sp].u.dict, tofrees);
	    ++sp;
	 }
      }
      if (sp<sizeof(stack)/sizeof(stack[0])) {
	 stack[sp].type=ps_array;
	 stack[sp].u.dict=dict;
	 ++sp;
      }
   }
   return (sp);
}

static void printarray(struct pskeydict *dict) {
   int i;

   printf("[");
   for (i=0; i<dict->cnt; ++i) {
      switch (dict->entries[i].type) {
	case ps_num:
	   printf("%g", (double) dict->entries[i].u.val);
	   break;
	case ps_bool:
	   printf("%s", dict->entries[i].u.tf?"true":"false");
	   break;
	case ps_string:
	case ps_instr:
	case ps_lit:
	   printf(dict->entries[i].type==ps_lit?"/" :
		  dict->entries[i].type==ps_string?"(":"{");
	   printf("%s", dict->entries[i].u.str);
	   printf(dict->entries[i].type==ps_lit?"" :
		  dict->entries[i].type==ps_string?")":"}");
	   break;
	case ps_array:
	   printarray(&dict->entries[i].u.dict);
	   break;
	case ps_void:
	   printf("-- void --");
	   break;
	default:
	   printf("-- nostringval --");
	   break;
      }
      printf(" ");
   }
   printf("]");
}

static void freestuff(struct psstack *stack,int sp,struct pskeydict *dict,
		      GrowBuf * gb, struct garbage *tofrees) {
   int i;

   free(gb->base);
   for (i=0; i<dict->cnt; ++i) {
      if (dict->entries[i].type==ps_string
	  || dict->entries[i].type==ps_instr
	  || dict->entries[i].type==ps_lit)
	 free(dict->entries[i].u.str);
      free(dict->entries[i].key);
   }
   free(dict->entries);
   for (i=0; i<sp; ++i) {
      if (stack[i].type==ps_string || stack[i].type==ps_instr ||
	  stack[i].type==ps_lit)
	 free(stack[i].u.str);
   }
   garbagefree(tofrees);
}

static void DoMatTransform(int tok,int sp,struct psstack *stack) {
   double invt[6], t[6];

   if (stack[sp-1].u.dict.cnt==6
       && stack[sp-1].u.dict.entries[0].type==ps_num) {
      double x=stack[sp-3].u.val, y=stack[sp-2].u.val;

      --sp;
      t[5]=stack[sp].u.dict.entries[5].u.val;
      t[4]=stack[sp].u.dict.entries[4].u.val;
      t[3]=stack[sp].u.dict.entries[3].u.val;
      t[2]=stack[sp].u.dict.entries[2].u.val;
      t[1]=stack[sp].u.dict.entries[1].u.val;
      t[0]=stack[sp].u.dict.entries[0].u.val;
      dictfree(&stack[sp].u.dict);
      if (tok==pt_itransform || tok==pt_idtransform) {
	 MatInverse(invt, t);
	 memcpy(t, invt, sizeof(t));
      }
      stack[sp-2].u.val=t[0] * x+t[1] * y;
      stack[sp-1].u.val=t[2] * x+t[3] * y;
      if (tok==pt_transform || tok==pt_itransform) {
	 stack[sp-2].u.val += t[4];
	 stack[sp-1].u.val += t[5];
      }
   }
}

static int DoMatOp(int tok,int sp,struct psstack *stack) {
   double temp[6], t[6];
   int nsp=sp;

   if (stack[sp-1].u.dict.cnt==6
       && stack[sp-1].u.dict.entries[0].type==ps_num) {
      t[5]=stack[sp-1].u.dict.entries[5].u.val;
      t[4]=stack[sp-1].u.dict.entries[4].u.val;
      t[3]=stack[sp-1].u.dict.entries[3].u.val;
      t[2]=stack[sp-1].u.dict.entries[2].u.val;
      t[1]=stack[sp-1].u.dict.entries[1].u.val;
      t[0]=stack[sp-1].u.dict.entries[0].u.val;
      switch (tok) {
	case pt_translate:
	   if (sp >= 3) {
	      stack[sp-1].u.dict.entries[5].u.val +=
		 stack[sp-3].u.val * t[0]+stack[sp-2].u.val * t[2];
	      stack[sp-1].u.dict.entries[4].u.val +=
		 stack[sp-3].u.val * t[1]+stack[sp-2].u.val * t[3];
	      nsp=sp-2;
	   }
	   break;
	case pt_scale:
	   if (sp >= 2) {
	      stack[sp-1].u.dict.entries[0].u.val *= stack[sp-3].u.val;
	      stack[sp-1].u.dict.entries[1].u.val *= stack[sp-3].u.val;
	      stack[sp-1].u.dict.entries[2].u.val *= stack[sp-2].u.val;
	      stack[sp-1].u.dict.entries[3].u.val *= stack[sp-2].u.val;
	      /* transform[4,5] are unchanged */
	      nsp=sp-2;
	   }
	   break;
	case pt_rotate:
	   if (sp >= 1) {
	      --sp;
	      temp[0]=temp[3]=cos(stack[sp].u.val);
	      temp[1]=sin(stack[sp].u.val);
	      temp[2]=-temp[1];
	      temp[4]=temp[5]=0;
	      MatMultiply(temp, t, t);
	      stack[sp-1].u.dict.entries[5].u.val=t[5];
	      stack[sp-1].u.dict.entries[4].u.val=t[4];
	      stack[sp-1].u.dict.entries[3].u.val=t[3];
	      stack[sp-1].u.dict.entries[2].u.val=t[2];
	      stack[sp-1].u.dict.entries[1].u.val=t[1];
	      stack[sp-1].u.dict.entries[0].u.val=t[0];
	      nsp=sp-1;
	   }
	   break;
      }
      stack[nsp-1]=stack[sp-1];
   }
   return (nsp);
}

static Entity *EntityCreate(SplinePointList *head,int linecap,int linejoin,
			    double linewidth, double * transform,
			    SplineSet * clippath) {
   Entity *ent=calloc(1, sizeof(Entity));

   ent->type=et_splines;
   ent->u.splines.splines=head;
   ent->u.splines.cap=linecap;
   ent->u.splines.join=linejoin;
   ent->u.splines.stroke_width=linewidth;
   ent->u.splines.fill.col=0xffffffff;
   ent->u.splines.stroke.col=0xffffffff;
   ent->u.splines.fill.opacity=1.0;
   ent->u.splines.stroke.opacity=1.0;
   ent->clippath=SplinePointListCopy(clippath);
   memcpy(ent->u.splines.transform, transform, 6*sizeof(double));
   return (ent);
}

static uint8_t *StringToBytes(struct psstack *stackel,int *len) {
   char *pt;
   uint8_t *upt, *base, *ret;
   int half, sofar, val, nesting;
   int i, j;

   pt=stackel->u.str;
   if (stackel->type==ps_instr) {
      /* imagemask operators take strings or procedures or files */
      /* we support strings, or procedures containing strings */
      while (isspace(*pt))
	 ++pt;
      if (*pt=='{' || *pt=='[')
	 ++pt;
      while (isspace(*pt))
	 ++pt;
   } else if (stackel->type != pt_string)
      return (NULL);

   upt=base=malloc(65536+1);	/* Maximum size of ps string */

   if (*pt=='(') {
      /* A conventional string */
      ++pt;
      nesting=0;
      while (*pt != '\0' && (nesting != 0 || *pt != ')')) {
	 if (*pt=='(') {
	    ++nesting;
	    *upt++=*pt++;
	 } else if (*pt==')') {
	    --nesting;
	    *upt++=*pt++;
	 } else if (*pt=='\r' || *pt=='\n') {
	    /* any of lf, cr, crlf gets translated to \n */
	    if (*pt=='\r' && pt[1]=='\n')
	       ++pt;
	    *upt++='\n';
	    ++pt;
	 } else if (*pt != '\\') {
	    *upt++=*pt++;
	 } else {
	    ++pt;
	    if (*pt=='\r' || *pt=='\n') {
	       /* any of \lf, \cr, \crlf gets ignored */
	       if (*pt=='\r' && pt[1]=='\n')
		  ++pt;
	       ++pt;
	    } else if (*pt=='n') {
	       *upt++='\n';
	       ++pt;
	    } else if (*pt=='r') {
	       *upt++='\r';
	       ++pt;
	    } else if (*pt=='t') {
	       *upt++='\t';
	       ++pt;
	    } else if (*pt=='b') {
	       *upt++='\b';
	       ++pt;
	    } else if (*pt=='f') {
	       *upt++='\f';
	       ++pt;
	    } else if (*pt >= '0' && *pt <= '7') {
	       if (pt[1]<'0' || pt[1]>'7')	/* This isn't really legal postscript */
		  *upt++=*pt++-'0';
	       else if (pt[2]<'0' || pt[2]>'7') {	/* 3 octal digits are required */
		  *upt++=((*pt-'0') << 3)+(pt[1]-'0');
		  pt += 2;
	       } else {
		  *upt++ =
		     ((*pt-'0') << 6)+((pt[1]-'0') << 3)+(pt[2] -
								  '0');
		  pt += 3;
	       }
	    } else if (*pt=='(' || *pt==')' || *pt=='\\')
	       *upt++=*pt++;
	    else {
	       ErrorMsg(2,"Unknown character after backslash in literal string.\n");
	       *upt++=*pt++;
	    }
	 }
      }
   } else if (*pt != '<') {
      ErrorMsg(2,"Unknown string type\n");
      free(base);
      return (NULL);
   } else if (pt[1] != '~') {
      /* A hex string. Ignore any characters which aren't hex */
      half=sofar=0;
      ++pt;
      while (*pt != '>' && *pt != '\0') {
	 if (*pt >= 'a' && *pt <= 'f')
	    val=*pt++-'a'+10;
	 else if (*pt >= 'A' && *pt <= 'F')
	    val=*pt++-'A'+10;
	 else if (isdigit(*pt))
	    val=*pt++-'0';
	 else {
	    ++pt;		/* Not hex */
	    continue;
	 }
	 if (!half) {
	    half=true;
	    sofar=val << 4;
	 } else {
	    *upt++=sofar|val;
	    half=false;
	 }
      }
      if (half)
	 *upt++=sofar;
   } else {
      /* An ASCII-85 string */
      /* c1 c2 c3 c4 c5 (c>='!' && c<='u') => ((c1-'!')*85+(c2-'!'))*85... */
      /* z => 32bits of 0 */
      pt += 2;
      while (*pt != '\0' && *pt != '~') {
	 if (upt-base+4>65536)
	    break;
	 if (*pt=='z') {
	    *upt++=0;
	    *upt++=0;
	    *upt++=0;
	    *upt++=0;
	    ++pt;
	 } else if (*pt >= '!' && *pt <= 'u') {
	    val=0;
	    for (i=0; i<5 && *pt >= '!' && *pt <= 'u'; ++i)
	       val=(val*85)+*pt++-'!';
	    for (j=i; j<5; ++j)
	       val *= 85;
	    *upt++=val >> 24;
	    if (i>2)
	       *upt++=(val >> 16)&0xff;
	    if (i>3)
	       *upt++=(val >> 8)&0xff;
	    if (i>4)
	       *upt++=val&0xff;
	    if (i<5)
	       break;
	 } else if (isspace(*pt)) {
	    ++pt;
	 } else
	    break;
      }
   }
   *len=upt-base;
   ret=malloc(upt-base);
   memcpy(ret, base, upt-base);
   free(base);
   return (ret);
}

static int PSAddImagemask(EntityChar *ec,struct psstack *stack,int sp,
			  double transform[6], Color fillcol) {
   uint8_t *data;
   int datalen, width, height, polarity;
   double trans[6];

   struct _GImage *base;
   GImage *gi;
   Entity *ent;
   int i, j;

   if (sp<5
       || (stack[sp-1].type != ps_instr
	   && stack[sp-1].type != ps_string)) {
      ErrorMsg(2,"FontAnvil does not support dictionary based imagemask operators.\n");
      return (sp-1);
   }

   if (stack[sp-2].type != ps_array || stack[sp-2].u.dict.cnt != 6) {
      ErrorMsg(2,"Fourth argument of imagemask must be a 6-element transformation matrix.\n");
      return (sp-5);
   }

   if (stack[sp-3].type != ps_bool) {
      ErrorMsg(2,"Third argument of imagemask must be a boolean.\n");
      return (sp-5);
   }
   polarity=stack[sp-3].u.tf;

   if (stack[sp-4].type != ps_num || stack[sp-5].type != ps_num) {
      ErrorMsg(2,"First and second arguments of imagemask must be integers.\n");
      return (sp-5);
   }
   height=stack[sp-4].u.val;
   width=stack[sp-5].u.val;

   data=StringToBytes(&stack[sp-1], &datalen);

   if (width <= 0 || height <= 0 || ((width+7)/8)*height>datalen) {
      ErrorMsg(2,"Width or height arguments to imagemask contain invalid values\n(either negative or they require more data than provided).\n");
      free(data);
      return (sp-5);
   }
   trans[0]=stack[sp-2].u.dict.entries[0].u.val;
   trans[1]=stack[sp-2].u.dict.entries[1].u.val;
   trans[2]=stack[sp-2].u.dict.entries[2].u.val;
   trans[3]=stack[sp-2].u.dict.entries[3].u.val;
   trans[4]=stack[sp-2].u.dict.entries[4].u.val;
   trans[5]=stack[sp-2].u.dict.entries[5].u.val;

   gi=GImageCreate(it_mono, width, height);
   base=gi->u.image;
   base->trans=1;
   if (polarity) {
      for (i=0; i<datalen; ++i)
	 data[i] ^= 0xff;
   }
   if (trans[0]>0 && trans[3]<0)
      memcpy(base->data, data, datalen);
   else if (trans[0]>0 && trans[3]>0) {
      for (i=0; i<height; ++i)
	 memcpy(base->data+i * base->bytes_per_line,
		data+(height-i)*base->bytes_per_line,
		base->bytes_per_line);
   } else if (trans[0]<0 && trans[3]<0) {
      for (i=0; i<height; ++i)
	 for (j=0; j<width; ++j) {
	    if (data[i * base->bytes_per_line+(j >> 3)]&(0x80 >> (j&7)))
	       base->data[i * base->bytes_per_line +
			  ((width-j-1) >> 3)] |=
		  (0x80 >> ((width-j-1)&7));
	 }
   } else {
      for (i=0; i<height; ++i)
	 for (j=0; j<width; ++j) {
	    if (data[i * base->bytes_per_line+(j >> 3)]&(0x80 >> (j&7)))
	       base->data[(height-i-1)*base->bytes_per_line +
			  ((width-j-1) >> 3)] |=
		  (0x80 >> ((width-j-1)&7));
	 }
   }
   free(data);

   ent=calloc(1, sizeof(Entity));
   ent->type=et_image;
   ent->u.image.image=gi;
   memcpy(ent->u.image.transform, transform, sizeof(double[6]));
   ent->u.image.transform[0] /= width;
   ent->u.image.transform[3] /= height;
   ent->u.image.transform[5] += height;
   ent->u.image.col=fillcol;

   ent->next=ec->splines;
   ec->splines=ent;
   return (sp-5);
}

static void HandleType3Reference(IO *wrapper,EntityChar *ec,
				 double transform[6], char *tokbuf,
				 int toksize) {
   int tok;
   double dval;
   char *glyphname;
   RefChar *ref;

   tok=nextpstoken(wrapper, &dval, tokbuf, toksize);
   if (strcmp(tokbuf, "get") != 0)
      return;			/* Hunh. I don't understand it. I give up */
   tok=nextpstoken(wrapper, &dval, tokbuf, toksize);
   if (tok != pt_namelit)
      return;			/* Hunh. I don't understand it. I give up */
   glyphname=fastrdup(tokbuf);
   tok=nextpstoken(wrapper, &dval, tokbuf, toksize);
   if (strcmp(tokbuf, "get") != 0) {
      free(glyphname);
      return;			/* Hunh. I don't understand it. I give up */
   }
   tok=nextpstoken(wrapper, &dval, tokbuf, toksize);
   if (strcmp(tokbuf, "exec") != 0) {
      free(glyphname);
      return;			/* Hunh. I don't understand it. I give up */
   }

   /* Ok, it looks very much like a reference to glyphname */
   ref=RefCharCreate();
   memcpy(ref->transform, transform, sizeof(ref->transform));
   ref->sc=(SplineChar *) glyphname;
   ref->next=ec->refs;
   ec->refs=ref;
}

static void _InterpretPS(IO *wrapper,EntityChar *ec,RetStack *rs) {
   SplinePointList *cur=NULL, *head=NULL;
   DBasePoint current, temp;
   int tok, i, j;
   struct psstack stack[100];
   double dval;
   int sp=0;
   SplinePoint *pt;
   RefChar *ref, *lastref=NULL;
   double transform[6], t[6];
   struct graphicsstate {
      double transform[6];
      DBasePoint current;
      double linewidth;
      int linecap, linejoin;
      Color fore;
      DashType dashes[DASH_MAX];
      SplineSet *clippath;
   } gsaves[30];
   int gsp=0;
   int ccnt=0;
   GrowBuf gb;
   struct pskeydict dict;
   struct pskeyval *kv;
   Color fore=COLOR_INHERITED;
   int linecap=lc_inherited, linejoin=lj_inherited;
   double linewidth=WIDTH_INHERITED;
   DashType dashes[DASH_MAX];
   int dash_offset=0;
   Entity *ent;
   int warned=0;
   struct garbage tofrees;
   SplineSet *clippath=NULL;
   char *tokbuf;
   const int tokbufsize=2*65536+10;

   tokbuf=malloc(tokbufsize);

   memset(&gb, '\0', sizeof(GrowBuf));
   memset(&dict, '\0', sizeof(dict));
   tofrees.cnt=0;
   tofrees.next=NULL;

   transform[0]=transform[3]=1.0;
   transform[1]=transform[2]=transform[4]=transform[5]=0;
   current.x=current.y=0;
   dashes[0]=0;
   dashes[1]=DASH_INHERITED;

   if (ec->fromtype3) {
      /* My type3 fonts have two things pushed on the stack when they */
      /*  start. One is a dictionary, the other a flag (number). If the */
      /*  flag is non-zero then we are a nested call (a reference char) */
      /*  if 0, we're normal. We don't want to do setcachedevice for */
      /*  reference chars.  We can't represent a dictionary on the stack */
      /*  so just push two 0s */
      stack[0].type=stack[1].type=ps_num;
      stack[0].u.val=stack[1].u.val=0;
      sp=2;
   }

   while ((tok=nextpstoken(wrapper, &dval, tokbuf, tokbufsize)) != pt_eof) {
      if (endedstopped(wrapper)) {
	 if (sp<sizeof(stack)/sizeof(stack[0])) {
	    stack[sp].type=ps_bool;
	    stack[sp++].u.tf=false;
	 }
      }
      if (sp>sizeof(stack)/sizeof(stack[0])*4/5) {
	 /* We don't interpret all of postscript */
	 /* Sometimes we leave garbage on the stack that a real PS interp */
	 /*  would have handled. If the stack gets too deep, clean out the */
	 /*  oldest entries */
	 sp=forgetstack(stack, sizeof(stack)/sizeof(stack[0])/3, sp);
      }
      if (ccnt>0) {
	 if (tok==pt_closecurly)
	    --ccnt;
	 else if (tok==pt_opencurly)
	    ++ccnt;
	 if (ccnt>0)
	    AddTok(&gb, tokbuf, tok==pt_namelit);
	 else {
	    if (sp<sizeof(stack)/sizeof(stack[0])) {
	       stack[sp].type=ps_instr;
	       if (gb.pt==NULL)
		  stack[sp++].u.str=fastrdup("");
	       else {
		  *gb.pt='\0';
		  gb.pt=gb.base;
		  stack[sp++].u.str=fastrdup(gb.base);
	       }
	    }
	 }
      } else if (tok==pt_unknown && (kv=lookup(&dict, tokbuf)) != NULL) {
	 if (kv->type==ps_instr)
	    pushio(wrapper, NULL, fastrdup(kv->u.str), 0);
	 else if (sp<sizeof(stack)/sizeof(stack[0])) {
	    stack[sp].type=kv->type;
	    stack[sp++].u=kv->u;
	    if (kv->type==ps_instr || kv->type==ps_lit
		|| kv->type==ps_string)
	       stack[sp-1].u.str=fastrdup(stack[sp-1].u.str);
	    else if (kv->type==ps_array || kv->type==ps_dict) {
	       copyarray(&stack[sp-1].u.dict, &stack[sp-1].u.dict,
			 &tofrees);
	       if (stack[sp-1].u.dict.is_executable)
		  sp =
		     aload(sp, stack, sizeof(stack)/sizeof(stack[0]),
			   &tofrees);
	    }
	 }
      } else {
	 if (tok==pt_unknown) {
	    if (strcmp(tokbuf, "Cache")==0)	/* Fontographer type3s */
	       tok=pt_setcachedevice;
	    else if (strcmp(tokbuf, "SetWid")==0) {
	       tok=pt_setcharwidth;
	       if (sp<sizeof(stack)/sizeof(stack[0])) {
		  stack[sp].type=ps_num;
		  stack[sp++].u.val=0;
	       }
	    } else if (strcmp(tokbuf, "rrcurveto")==0) {
	       if (sp >= 6) {
		  stack[sp-4].u.val += stack[sp-6].u.val;
		  stack[sp-3].u.val += stack[sp-5].u.val;
		  stack[sp-2].u.val += stack[sp-4].u.val;
		  stack[sp-1].u.val += stack[sp-3].u.val;
		  tok=pt_rcurveto;
	       }
	    } else if (strcmp(tokbuf, "FillStroke")==0) {
	       if (sp>0)
		  --sp;
	       tok=linewidth != WIDTH_INHERITED?pt_stroke:pt_fill;
	       if (wrapper->top != NULL && wrapper->top->ps != NULL &&
		   linewidth != WIDTH_INHERITED)
		  linewidth /= 10.0;	/* bug in Fontographer's unencrypted type3 fonts */
	    } else if (strcmp(tokbuf, "SG")==0) {
	       if (linewidth != WIDTH_INHERITED && sp>1)
		  stack[sp-2].u.val=stack[sp-1].u.val;
	       if (sp>0)
		  --sp;
	       if (sp>0)
		  stack[sp-1].u.val=(stack[sp-1].u.val+99)/198.0;
	       tok=pt_setgray;
	    } else if (strcmp(tokbuf, "ShowInt")==0) {
	       /* Fontographer reference */
	       if ((!wrapper->top->fogns && sp>0
		    && stack[sp-1].type==ps_num
		    && stack[sp-1].u.val >= 0 && stack[sp-1].u.val <= 255)
		   || (wrapper->top->fogns && sp>6
		       && stack[sp-7].type==ps_num
		       && stack[sp-7].u.val >= 0
		       && stack[sp-7].u.val <= 255)) {
		  ref=RefCharCreate();
		  memcpy(ref->transform, transform, sizeof(ref->transform));
		  if (wrapper->top->fogns) {
		     sp -= 6;
		     t[0]=stack[sp+0].u.val;
		     t[1]=stack[sp+1].u.val;
		     t[2]=stack[sp+2].u.val;
		     t[3]=stack[sp+3].u.val;
		     t[4]=stack[sp+4].u.val;
		     t[5]=stack[sp+5].u.val;
		     MatMultiply(t, ref->transform, ref->transform);
		     wrapper->top->fogns=false;
		  }
		  ref->orig_pos=stack[--sp].u.val;
		  ref->next=ec->refs;
		  ec->refs=ref;
		  continue;
	       }
	    } else if (strcmp(tokbuf, "togNS_")==0) {
	       wrapper->top->fogns=!wrapper->top->fogns;
	       continue;
	    }
	 }
	 switch (tok) {
	   case pt_number:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=dval;
	      }
	      break;
	   case pt_string:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_string;
		 stack[sp++].u.str=copyn(tokbuf+1, strlen(tokbuf)-2);
	      }
	      break;
	   case pt_true:
	   case pt_false:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_bool;
		 stack[sp++].u.tf=tok==pt_true;
	      }
	      break;
	   case pt_opencurly:
	      ++ccnt;
	      break;
	   case pt_closecurly:
	      --ccnt;
	      if (ccnt<0) {
		 goto done;
	      }
	      break;
	   case pt_count:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp].u.val=sp;
		 ++sp;
	      }
	      break;
	   case pt_pop:
	      if (sp>0) {
		 --sp;
		 if (stack[sp].type==ps_string || stack[sp].type==ps_instr
		     || stack[sp].type==ps_lit)
		    free(stack[sp].u.str);
		 else if (stack[sp].type==ps_array
			  || stack[sp].type==ps_dict)
		    dictfree(&stack[sp].u.dict);
	      }
	      break;
	   case pt_clear:
	      while (sp>0) {
		 --sp;
		 if (stack[sp].type==ps_string || stack[sp].type==ps_instr
		     || stack[sp].type==ps_lit)
		    free(stack[sp].u.str);
		 else if (stack[sp].type==ps_array
			  || stack[sp].type==ps_dict)
		    dictfree(&stack[sp].u.dict);
	      }
	      break;
	   case pt_dup:
	      if (sp>0 && sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp]=stack[sp-1];
		 if (stack[sp].type==ps_string || stack[sp].type==ps_instr
		     || stack[sp].type==ps_lit)
		    stack[sp].u.str=fastrdup(stack[sp].u.str);
		 /* The following is incorrect behavior, but as I don't do garbage collection */
		 /*  and I'm not going to implement reference counts, this will work in most cases */
		 else if (stack[sp].type==ps_array)
		    copyarray(&stack[sp].u.dict, &stack[sp].u.dict, &tofrees);
		 ++sp;
	      }
	      break;
	   case pt_copy:
	      if (sp>0) {
		 int n=stack[--sp].u.val;

		 if (n+sp<sizeof(stack)/sizeof(stack[0])) {
		    int i;

		    for (i=0; i<n; ++i) {
		       stack[sp]=stack[sp-n];
		       if (stack[sp].type==ps_string
			   || stack[sp].type==ps_instr
			   || stack[sp].type==ps_lit)
			  stack[sp].u.str=fastrdup(stack[sp].u.str);
		       /* The following is incorrect behavior, but as I don't do garbage collection */
		       /*  and I'm not going to implement reference counts, this will work in most cases */
		       else if (stack[sp].type==ps_array)
			  copyarray(&stack[sp].u.dict, &stack[sp].u.dict,
				    &tofrees);
		       ++sp;
		    }
		 }
	      }
	      break;
	   case pt_exch:
	      if (sp>1) {
		 struct psstack temp;

		 temp=stack[sp-1];
		 stack[sp-1]=stack[sp-2];
		 stack[sp-2]=temp;
	      }
	      break;
	   case pt_roll:
	      sp=rollstack(stack, sp);
	      break;
	   case pt_index:
	      if (sp>0) {
		 i=stack[--sp].u.val;
		 if (sp>i && i >= 0) {
		    stack[sp]=stack[sp-i-1];
		    if (stack[sp].type==ps_string
			|| stack[sp].type==ps_instr
			|| stack[sp].type==ps_lit)
		       stack[sp].u.str=fastrdup(stack[sp].u.str);
		    /* The following is incorrect behavior, but as I don't do garbage collection */
		    /*  and I'm not going to implement reference counts, this will work in most cases */
		    else if (stack[sp].type==ps_array)
		       copyarray(&stack[sp].u.dict, &stack[sp].u.dict,
				 &tofrees);
		    ++sp;
		 }
	      }
	      break;
	   case pt_add:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 stack[sp-2].u.val += stack[sp-1].u.val;
		 --sp;
	      }
	      break;
	   case pt_sub:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 stack[sp-2].u.val -= stack[sp-1].u.val;
		 --sp;
	      }
	      break;
	   case pt_mul:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 stack[sp-2].u.val *= stack[sp-1].u.val;
		 --sp;
	      }
	      break;
	   case pt_div:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 if (stack[sp-1].u.val==0)
		    ErrorMsg(2,"Divide by zero in postscript code.\n");
		 else
		    stack[sp-2].u.val /= stack[sp-1].u.val;
		 --sp;
	      }
	      break;
	   case pt_idiv:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 if (stack[sp-1].u.val==0)
		    ErrorMsg(2,"Divide by zero in postscript code.\n");
		 else
		    stack[sp-2].u.val =
		       ((int) stack[sp-2].u.val) /
		       ((int) stack[sp-1].u.val);
		 --sp;
	      }
	      break;
	   case pt_mod:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 if (stack[sp-1].u.val==0)
		    ErrorMsg(2,"Divide by zero in postscript code.\n");
		 else
		    stack[sp-2].u.val =
		       ((int) stack[sp-2].u.val) %
		       ((int) stack[sp-1].u.val);
		 --sp;
	      }
	      break;
	   case pt_max:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 if (stack[sp-2].u.val<stack[sp-1].u.val)
		    stack[sp-2].u.val=stack[sp-1].u.val;
		 --sp;
	      }
	      break;
	   case pt_min:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 if (stack[sp-2].u.val>stack[sp-1].u.val)
		    stack[sp-2].u.val=stack[sp-1].u.val;
		 --sp;
	      }
	      break;
	   case pt_neg:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_num)
		    stack[sp-1].u.val=-stack[sp-1].u.val;
	      }
	      break;
	   case pt_abs:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_num)
		    if (stack[sp-1].u.val<0)
		       stack[sp-1].u.val=-stack[sp-1].u.val;
	      }
	      break;
	   case pt_round:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_num)
		    stack[sp-1].u.val=rint(stack[sp-1].u.val);
		 /* rint isn't quite right, round will take 6.5 to 7, 5.5 to 6, etc. while rint() will take both to 6 */
	      }
	      break;
	   case pt_floor:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_num)
		    stack[sp-1].u.val=floor(stack[sp-1].u.val);
	      }
	      break;
	   case pt_ceiling:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_num)
		    stack[sp-1].u.val=ceil(stack[sp-1].u.val);
	      }
	      break;
	   case pt_truncate:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_num) {
		    if (stack[sp-1].u.val<0)
		       stack[sp-1].u.val=ceil(stack[sp-1].u.val);
		    else
		       stack[sp-1].u.val=floor(stack[sp-1].u.val);
		 }
	      }
	      break;
	   case pt_ne:
	   case pt_eq:
	      if (sp >= 2) {
		 if (stack[sp-2].type != stack[sp-1].type)
		    stack[sp-2].u.tf=false;
		 else if (stack[sp-2].type==ps_num)
		    stack[sp-2].u.tf =
		       (stack[sp-2].u.val==stack[sp-1].u.val);
		 else if (stack[sp-2].type==ps_bool)
		    stack[sp-2].u.tf =
		       (stack[sp-2].u.tf==stack[sp-1].u.tf);
		 else
		    stack[sp-2].u.tf =
		       strcmp(stack[sp-2].u.str, stack[sp-1].u.str)==0;
		 stack[sp-2].type=ps_bool;
		 if (tok==pt_ne)
		    stack[sp-2].u.tf=!stack[sp-2].u.tf;
		 --sp;
	      }
	      break;
	   case pt_gt:
	   case pt_le:
	   case pt_lt:
	   case pt_ge:
	      if (sp >= 2) {
		 if (stack[sp-2].type != stack[sp-1].type)
		    stack[sp-2].u.tf=false;
		 else if (stack[sp-2].type==ps_array)
		    ErrorMsg(2,"Can't compare arrays\n");
		 else {
		    int cmp;

		    if (stack[sp-2].type==ps_num)
		       cmp=(stack[sp-2].u.val>stack[sp-1].u.val)?1 :
			  (stack[sp-2].u.val ==
			   stack[sp-1].u.val)?0:-1;
		    else if (stack[sp-2].type==ps_bool)
		       cmp=(stack[sp-2].u.tf-stack[sp-1].u.tf);
		    else
		       cmp=strcmp(stack[sp-2].u.str, stack[sp-1].u.str);
		    if (tok==pt_gt)
		       stack[sp-2].u.tf=cmp>0;
		    else if (tok==pt_lt)
		       stack[sp-2].u.tf=cmp<0;
		    else if (tok==pt_le)
		       stack[sp-2].u.tf=cmp <= 0;
		    else
		       stack[sp-2].u.tf=cmp >= 0;
		 }
		 stack[sp-2].type=ps_bool;
		 --sp;
	      }
	      break;
	   case pt_not:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_bool)
		    stack[sp-1].u.tf=!stack[sp-1].u.tf;
	      }
	      break;
	   case pt_and:
	      if (sp >= 2) {
		 if (stack[sp-2].type==ps_num)
		    stack[sp-2].u.val =
		       ((int) stack[sp-1].u.val)&(int) stack[sp -
								 1].u.val;
		 else if (stack[sp-2].type==ps_bool)
		    stack[sp-2].u.tf &= stack[sp-1].u.tf;
		 --sp;
	      }
	      break;
	   case pt_or:
	      if (sp >= 2) {
		 if (stack[sp-2].type==ps_num)
		    stack[sp-2].u.val =
		       ((int) stack[sp-1].u.val)|(int) stack[sp -
								 1].u.val;
		 else if (stack[sp-2].type==ps_bool)
		    stack[sp-2].u.tf |= stack[sp-1].u.tf;
		 --sp;
	      }
	      break;
	   case pt_xor:
	      if (sp >= 2) {
		 if (stack[sp-2].type==ps_num)
		    stack[sp-2].u.val =
		       ((int) stack[sp-1].u.val) ^ (int) stack[sp -
								 1].u.val;
		 else if (stack[sp-2].type==ps_bool)
		    stack[sp-2].u.tf ^= stack[sp-1].u.tf;
		 --sp;
	      }
	      break;
	   case pt_exp:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 stack[sp-2].u.val =
		    pow(stack[sp-2].u.val, stack[sp-1].u.val);
		 --sp;
	      }
	      break;
	   case pt_sqrt:
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 stack[sp-1].u.val=sqrt(stack[sp-1].u.val);
	      }
	      break;
	   case pt_ln:
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 stack[sp-1].u.val=log(stack[sp-1].u.val);
	      }
	      break;
	   case pt_log:
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 stack[sp-1].u.val=log10(stack[sp-1].u.val);
	      }
	      break;
	   case pt_atan:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_num) {
		 stack[sp-2].u.val =
		    atan2(stack[sp-2].u.val,
			  stack[sp-1].u.val)*180/M_PI;
		 --sp;
	      }
	      break;
	   case pt_sin:
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 stack[sp-1].u.val =
		    sin(stack[sp-1].u.val * M_PI/180);
	      }
	      break;
	   case pt_cos:
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 stack[sp-1].u.val =
		    cos(stack[sp-1].u.val * M_PI/180);
	      }
	      break;
	   case pt_if:
	      if (sp >= 2) {
		 if (((stack[sp-2].type==ps_bool && stack[sp-2].u.tf) ||
		      (stack[sp-2].type==ps_num
		       && strstr(stack[sp-1].u.str,
				 "setcachedevice") != NULL))
		     && stack[sp-1].type==ps_instr)
		    pushio(wrapper, NULL, stack[sp-1].u.str, 0);
		 if (stack[sp-1].type==ps_string
		     || stack[sp-1].type==ps_instr
		     || stack[sp-1].type==ps_lit)
		    free(stack[sp-1].u.str);
		 sp -= 2;
	      } else if (sp==1 && stack[sp-1].type==ps_instr) {
		 /*This can happen when reading our type3 fonts, we get passed */
		 /* values on the stack which the interpreter knows nothing */
		 /* about, but the interp needs to learn the width of the char */
		 if (strstr(stack[sp-1].u.str, "setcachedevice") != NULL ||
		     strstr(stack[sp-1].u.str, "setcharwidth") != NULL)
		    pushio(wrapper, NULL, stack[sp-1].u.str, 0);
		 free(stack[sp-1].u.str);
		 sp=0;
	      }
	      break;
	   case pt_ifelse:
	      if (sp >= 3) {
		 if (stack[sp-3].type==ps_bool && stack[sp-3].u.tf) {
		    if (stack[sp-2].type==ps_instr)
		       pushio(wrapper, NULL, stack[sp-2].u.str, 0);
		 } else {
		    if (stack[sp-1].type==ps_instr)
		       pushio(wrapper, NULL, stack[sp-1].u.str, 0);
		 }
		 if (stack[sp-1].type==ps_string
		     || stack[sp-1].type==ps_instr
		     || stack[sp-1].type==ps_lit)
		    free(stack[sp-1].u.str);
		 if (stack[sp-2].type==ps_string
		     || stack[sp-2].type==ps_instr
		     || stack[sp-2].type==ps_lit)
		    free(stack[sp-2].u.str);
		 sp -= 3;
	      }
	      break;
	   case pt_for:
	      if (sp >= 4) {
		 double init, incr, limit;
		 char *func;
		 int cnt;

		 if (stack[sp-4].type==ps_num
		     && stack[sp-3].type==ps_num
		     && stack[sp-2].type==ps_num
		     && stack[sp-1].type==ps_instr) {
		    init=stack[sp-4].u.val;
		    incr=stack[sp-3].u.val;
		    limit=stack[sp-2].u.val;
		    func=stack[sp-1].u.str;
		    sp -= 4;
		    cnt=0;
		    if (incr>0) {
		       while (init <= limit) {
			  ++cnt;
			  init += incr;
		       }
		    } else if (incr<0) {
		       while (init >= limit) {
			  ++cnt;
			  init += incr;
		       }
		    }
		    pushio(wrapper, NULL, func, cnt);
		    free(func);
		 }
	      }
	      break;
	   case pt_loop:
	      if (sp >= 1) {
		 char *func;
		 int cnt;

		 if (stack[sp-1].type==ps_instr) {
		    cnt=0x7fffffff;	/* Loop for ever */
		    func=stack[sp-1].u.str;
		    --sp;
		    pushio(wrapper, NULL, func, cnt);
		    free(func);
		 }
	      }
	      break;
	   case pt_repeat:
	      if (sp >= 2) {
		 char *func;
		 int cnt;

		 if (stack[sp-2].type==ps_num
		     && stack[sp-1].type==ps_instr) {
		    cnt=stack[sp-2].u.val;
		    func=stack[sp-1].u.str;
		    sp -= 2;
		    pushio(wrapper, NULL, func, cnt);
		    free(func);
		 }
	      }
	      break;
	   case pt_exit:
	      ioescapeloop(wrapper);
	      break;
	   case pt_stopped:
	      if (sp >= 1) {
		 char *func;

		 if (stack[sp-1].type==ps_instr) {
		    func=stack[sp-1].u.str;
		    --sp;
		    pushio(wrapper, NULL, func, -1);
		    free(func);
		 }
	      }
	      break;
	   case pt_stop:
	      sp =
		 ioescapestopped(wrapper, stack, sp,
				 sizeof(stack)/sizeof(stack[0]));
	      break;
	   case pt_load:
	      if (sp >= 1 && stack[sp-1].type==ps_lit) {
		 kv=lookup(&dict, stack[sp-1].u.str);
		 if (kv != NULL) {
		    free(stack[sp-1].u.str);
		    stack[sp-1].type=kv->type;
		    stack[sp-1].u=kv->u;
		    if (kv->type==ps_instr || kv->type==ps_lit)
		       stack[sp-1].u.str=fastrdup(stack[sp-1].u.str);
		 } else
		    stack[sp-1].type=ps_instr;
	      }
	      break;
	   case pt_def:
	      sp=AddEntry(&dict, stack, sp);
	      break;
	   case pt_bind:
	      /* a noop in this context */
	      break;
	   case pt_setcachedevice:
	      if (sp >= 6) {
		 ec->width=stack[sp-6].u.val;
		 ec->vwidth=stack[sp-5].u.val;
		 /* I don't care about the bounding box */
		 sp -= 6;
	      }
	      break;
	   case pt_setcharwidth:
	      if (sp >= 2)
		 ec->width=stack[sp -= 2].u.val;
	      break;
	   case pt_translate:
	      if (sp >= 1 && stack[sp-1].type==ps_array)
		 sp=DoMatOp(tok, sp, stack);
	      else if (sp >= 2) {
		 transform[4] +=
		    stack[sp-2].u.val * transform[0]+stack[sp -
							       1].u.val *
		    transform[2];
		 transform[5] +=
		    stack[sp-2].u.val * transform[1]+stack[sp -
							       1].u.val *
		    transform[3];
		 sp -= 2;
	      }
	      break;
	   case pt_scale:
	      if (sp >= 1 && stack[sp-1].type==ps_array)
		 sp=DoMatOp(tok, sp, stack);
	      else if (sp >= 2) {
		 transform[0] *= stack[sp-2].u.val;
		 transform[1] *= stack[sp-2].u.val;
		 transform[2] *= stack[sp-1].u.val;
		 transform[3] *= stack[sp-1].u.val;
		 /* transform[4,5] are unchanged */
		 sp -= 2;
	      }
	      break;
	   case pt_rotate:
	      if (sp >= 1 && stack[sp-1].type==ps_array)
		 sp=DoMatOp(tok, sp, stack);
	      else if (sp >= 1) {
		 --sp;
		 t[0]=t[3]=cos(stack[sp].u.val);
		 t[1]=sin(stack[sp].u.val);
		 t[2]=-t[1];
		 t[4]=t[5]=0;
		 MatMultiply(t, transform, transform);
	      }
	      break;
	   case pt_concat:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_array) {
		    if (stack[sp-1].u.dict.cnt==6
			&& stack[sp-1].u.dict.entries[0].type==ps_num) {
		       --sp;
		       t[5]=stack[sp].u.dict.entries[5].u.val;
		       t[4]=stack[sp].u.dict.entries[4].u.val;
		       t[3]=stack[sp].u.dict.entries[3].u.val;
		       t[2]=stack[sp].u.dict.entries[2].u.val;
		       t[1]=stack[sp].u.dict.entries[1].u.val;
		       t[0]=stack[sp].u.dict.entries[0].u.val;
		       dictfree(&stack[sp].u.dict);
		       MatMultiply(t, transform, transform);
		    }
		 }
	      }
	      break;
	   case pt_transform:
	      if (sp >= 1 && stack[sp-1].type==ps_array) {
		 if (sp >= 3) {
		    DoMatTransform(tok, sp, stack);
		    --sp;
		 }
	      } else if (sp >= 2) {
		 double x=stack[sp-2].u.val, y=stack[sp-1].u.val;

		 stack[sp-2].u.val =
		    transform[0] * x+transform[1] * y+transform[4];
		 stack[sp-1].u.val =
		    transform[2] * x+transform[3] * y+transform[5];
	      }
	      break;
	   case pt_itransform:
	      if (sp >= 1 && stack[sp-1].type==ps_array) {
		 if (sp >= 3) {
		    DoMatTransform(tok, sp, stack);
		    --sp;
		 }
	      } else if (sp >= 2) {
		 double x=stack[sp-2].u.val, y=stack[sp-1].u.val;

		 MatInverse(t, transform);
		 stack[sp-2].u.val=t[0] * x+t[1] * y+t[4];
		 stack[sp-1].u.val=t[2] * x+t[3] * y+t[5];
	      }
	      break;
	   case pt_dtransform:
	      if (sp >= 1 && stack[sp-1].type==ps_array) {
		 if (sp >= 3) {
		    DoMatTransform(tok, sp, stack);
		    --sp;
		 }
	      } else if (sp >= 2) {
		 double x=stack[sp-2].u.val, y=stack[sp-1].u.val;

		 stack[sp-2].u.val=transform[0] * x+transform[1] * y;
		 stack[sp-1].u.val=transform[2] * x+transform[3] * y;
	      }
	      break;
	   case pt_idtransform:
	      if (sp >= 1 && stack[sp-1].type==ps_array) {
		 if (sp >= 3) {
		    DoMatTransform(tok, sp, stack);
		    --sp;
		 }
	      } else if (sp >= 2) {
		 double x=stack[sp-2].u.val, y=stack[sp-1].u.val;

		 MatInverse(t, transform);
		 stack[sp-2].u.val=t[0] * x+t[1] * y;
		 stack[sp-1].u.val=t[2] * x+t[3] * y;
	      }
	      break;
	   case pt_namelit:
	      if (strcmp(tokbuf, "CharProcs")==0 && ec != NULL) {
		 HandleType3Reference(wrapper, ec, transform, tokbuf,
				      tokbufsize);
	      } else if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_lit;
		 stack[sp++].u.str=fastrdup(tokbuf);
	      }
	      break;
	   case pt_exec:
	      if (sp>0 && stack[sp-1].type==ps_lit) {
		 ref=RefCharCreate();
		 ref->sc=(SplineChar *) stack[--sp].u.str;
		 memcpy(ref->transform, transform, sizeof(transform));
		 if (ec->refs==NULL)
		    ec->refs=ref;
		 else
		    lastref->next=ref;
		 lastref=ref;
	      }
	      break;
	   case pt_newpath:
	      SplinePointListsFree(head);
	      head=NULL;
	      cur=NULL;
	      break;
	   case pt_lineto:
	   case pt_rlineto:
	   case pt_moveto:
	   case pt_rmoveto:
	      if (sp >= 2 || tok==pt_newpath) {
		 if (tok==pt_rlineto || tok==pt_rmoveto) {
		    current.x += stack[sp-2].u.val;
		    current.y += stack[sp-1].u.val;
		    sp -= 2;
		 } else if (tok==pt_lineto || tok==pt_moveto) {
		    current.x=stack[sp-2].u.val;
		    current.y=stack[sp-1].u.val;
		    sp -= 2;
		 }
		 pt=chunkalloc(sizeof(SplinePoint));
		 Transform(&pt->me, &current, transform);
		 pt->noprevcp=true;
		 pt->nonextcp=true;
		 if (tok==pt_moveto || tok==pt_rmoveto) {
		    SplinePointList *spl =
		       chunkalloc(sizeof(SplinePointList));
		    spl->first=spl->last=pt;
		    if (cur != NULL)
		       cur->next=spl;
		    else
		       head=spl;
		    cur=spl;
		 } else {
		    if (cur != NULL && cur->first != NULL
			&& (cur->first != cur->last
			    || cur->first->next==NULL)) {
		       CheckMake(cur->last, pt);
		       SplineMake3(cur->last, pt);
		       cur->last=pt;
		    }
		 }
	      } else
		 sp=0;
	      break;
	   case pt_curveto:
	   case pt_rcurveto:
	      if (sp >= 6) {
		 if (tok==pt_rcurveto) {
		    stack[sp-1].u.val += current.y;
		    stack[sp-3].u.val += current.y;
		    stack[sp-5].u.val += current.y;
		    stack[sp-2].u.val += current.x;
		    stack[sp-4].u.val += current.x;
		    stack[sp-6].u.val += current.x;
		 }
		 current.x=stack[sp-2].u.val;
		 current.y=stack[sp-1].u.val;
		 if (cur != NULL && cur->first != NULL
		     && (cur->first != cur->last
			 || cur->first->next==NULL)) {
		    temp.x=stack[sp-6].u.val;
		    temp.y=stack[sp-5].u.val;
		    Transform(&cur->last->nextcp, &temp, transform);
		    cur->last->nonextcp=false;
		    pt=chunkalloc(sizeof(SplinePoint));
		    temp.x=stack[sp-4].u.val;
		    temp.y=stack[sp-3].u.val;
		    Transform(&pt->prevcp, &temp, transform);
		    Transform(&pt->me, &current, transform);
		    pt->nonextcp=true;
		    CheckMake(cur->last, pt);
		    SplineMake3(cur->last, pt);
		    cur->last=pt;
		 }
		 sp -= 6;
	      } else
		 sp=0;
	      break;
	   case pt_arc:
	   case pt_arcn:
	      if (sp >= 5) {
		 double cx, cy, r, a1, a2;

		 cx=stack[sp-5].u.val;
		 cy=stack[sp-4].u.val;
		 r=stack[sp-3].u.val;
		 a1=stack[sp-2].u.val;
		 a2=stack[sp-1].u.val;
		 sp -= 5;
		 temp.x=cx+r * cos(a1/180*M_PI);
		 temp.y=cy+r * sin(a1/180*M_PI);
		 if (temp.x != current.x || temp.y != current.y ||
		     !(cur != NULL && cur->first != NULL
		       && (cur->first != cur->last
			   || cur->first->next==NULL))) {
		    pt=chunkalloc(sizeof(SplinePoint));
		    Transform(&pt->me, &temp, transform);
		    pt->noprevcp=true;
		    pt->nonextcp=true;
		    if (cur != NULL && cur->first != NULL
			&& (cur->first != cur->last
			    || cur->first->next==NULL)) {
		       CheckMake(cur->last, pt);
		       SplineMake3(cur->last, pt);
		       cur->last=pt;
		    } else {	/* if no current point, then start here */
		       SplinePointList *spl =
			  chunkalloc(sizeof(SplinePointList));
		       spl->first=spl->last=pt;
		       if (cur != NULL)
			  cur->next=spl;
		       else
			  head=spl;
		       cur=spl;
		    }
		 }
		 circlearcsto(a1, a2, cx, cy, r, cur, transform,
			      tok==pt_arcn);
		 current.x=cx+r * cos(a2/180*M_PI);
		 current.y=cy+r * sin(a2/180*M_PI);
	      } else
		 sp=0;
	      break;
	   case pt_arct:
	   case pt_arcto:
	      if (sp >= 5) {
		 double x1, y1, x2, y2, r;
		 double xt1, xt2, yt1, yt2;

		 x1=stack[sp-5].u.val;
		 y1=stack[sp-4].u.val;
		 x2=stack[sp-3].u.val;
		 y2=stack[sp-2].u.val;
		 r=stack[sp-1].u.val;
		 sp -= 5;

		 xt1=xt2=x1;
		 yt1=yt2=y1;
		 if (cur==NULL || cur->first==NULL
		     || (cur->first==cur->last && cur->first->next != NULL))
		    /* Error */ ;
		 else if (current.x==x1 && current.y==y1)
		    /* Error */ ;
		 else if ((x1==x2 && y1==y2) ||
			  (current.x-x1)*(y2-y1) ==
			  (x2-x1)*(current.y-y1)) {
		    /* Degenerate case */
		    current.x=x1;
		    current.y=y1;
		    pt=chunkalloc(sizeof(SplinePoint));
		    Transform(&pt->me, &current, transform);
		    pt->noprevcp=true;
		    pt->nonextcp=true;
		    CheckMake(cur->last, pt);
		    SplineMake3(cur->last, pt);
		    cur->last=pt;
		 } else {
		    double l1 =
		       sqrt((current.x-x1)*(current.x-x1) +
			    (current.y-y1)*(current.y-y1));
		    double l2 =
		       sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
		    double dx=((current.x-x1)/l1+(x2-x1)/l2);
		    double dy=((current.y-y1)/l1+(y2-y1)/l2);

		    /* the line from (x1,y1) to (x1+dx,y1+dy) contains the center */
		    double l3=sqrt(dx * dx+dy * dy);
		    double cx, cy, t, tmid;
		    double a1, amid, a2;
		    int clockwise=true;

		    dx /= l3;
		    dy /= l3;
		    a1=atan2(current.y-y1, current.x-x1);
		    a2=atan2(y2-y1, x2-x1);
		    amid=atan2(dy, dx)-a1;
		    tmid=r/sin(amid);
		    t=r/tan(amid);
		    if (t<0) {
		       clockwise=false;
		       t=-t;
		       tmid=-tmid;
		    }
		    cx=x1+tmid * dx;
		    cy=y1+tmid * dy;
		    xt1=x1+t * (current.x-x1)/l1;
		    yt1=y1+t * (current.y-y1)/l1;
		    xt2=x1+t * (x2-x1)/l2;
		    yt2=y1+t * (y2-y1)/l2;
		    if (xt1 != current.x || yt1 != current.y) {
		       DBasePoint temp;

		       temp.x=xt1;
		       temp.y=yt1;
		       pt=chunkalloc(sizeof(SplinePoint));
		       Transform(&pt->me, &temp, transform);
		       pt->noprevcp=true;
		       pt->nonextcp=true;
		       CheckMake(cur->last, pt);
		       SplineMake3(cur->last, pt);
		       cur->last=pt;
		    }
		    a1=3*M_PI/2+a1;
		    a2=M_PI/2+a2;
		    if (!clockwise) {
		       a1 += M_PI;
		       a2 += M_PI;
		    }
		    circlearcsto(a1*180/M_PI,
				 a2*180/M_PI, cx, cy, r,
				 cur, transform, clockwise);
		 }
		 if (tok==pt_arcto) {
		    stack[sp].type=stack[sp+1].type=stack[sp+2].type =
		       stack[sp+3].type=ps_num;
		    stack[sp++].u.val=xt1;
		    stack[sp++].u.val=yt1;
		    stack[sp++].u.val=xt2;
		    stack[sp++].u.val=yt2;
		 }
		 current.x=xt2;
		 current.y=yt2;
	      }
	      break;
	   case pt_closepath:
	      if (cur != NULL && cur->first != NULL
		  && cur->first != cur->last) {
		 if (RealNear(cur->first->me.x, cur->last->me.x)
		     && RealNear(cur->first->me.y, cur->last->me.y)) {
		    SplinePoint *oldlast=cur->last;

		    cur->first->prevcp=oldlast->prevcp;
		    cur->first->prevcp.x +=
		       (cur->first->me.x-oldlast->me.x);
		    cur->first->prevcp.y +=
		       (cur->first->me.y-oldlast->me.y);
		    cur->first->noprevcp=oldlast->noprevcp;
		    oldlast->prev->from->next=NULL;
		    cur->last=oldlast->prev->from;
		    SplineFree(oldlast->prev);
		    SplinePointFree(oldlast);
		 }
		 CheckMake(cur->last, cur->first);
		 SplineMake3(cur->last, cur->first);
		 cur->last=cur->first;
	      }
	      break;
	   case pt_setlinecap:
	      if (sp >= 1)
		 linecap=stack[--sp].u.val;
	      break;
	   case pt_setlinejoin:
	      if (sp >= 1)
		 linejoin=stack[--sp].u.val;
	      break;
	   case pt_setlinewidth:
	      if (sp >= 1)
		 linewidth=stack[--sp].u.val;
	      break;
	   case pt_setdash:
	      if (sp >= 2 && stack[sp-1].type==ps_num
		  && stack[sp-2].type==ps_array) {
		 sp -= 2;
		 dash_offset=stack[sp+1].u.val;
		 for (i=0; i<DASH_MAX && i<stack[sp].u.dict.cnt; ++i)
		    dashes[i]=stack[sp].u.dict.entries[i].u.val;
		 dictfree(&stack[sp].u.dict);
	      }
	      break;
	   case pt_currentlinecap:
	   case pt_currentlinejoin:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val =
		    tok==pt_currentlinecap?linecap:linejoin;
	      }
	      break;
	   case pt_currentlinewidth:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=linewidth;
	      }
	      break;
	   case pt_currentdash:
	      if (sp+1<sizeof(stack)/sizeof(stack[0])) {
		 struct pskeydict dict;

		 for (i=0; i<DASH_MAX && dashes[i] != 0; ++i);
		 dict.cnt=dict.max=i;
		 dict.entries=calloc(i, sizeof(struct pskeyval));
		 for (j=0; j<i; ++j) {
		    dict.entries[j].type=ps_num;
		    dict.entries[j].u.val=dashes[j];
		 }
		 stack[sp].type=ps_array;
		 stack[sp++].u.dict=dict;
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=dash_offset;
	      }
	      break;
	   case pt_currentgray:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val =
		    (3*((fore >> 16)&0xff)+6*((fore >> 8)&0xff) +
		     (fore&0xff))/2550.;
	      }
	      break;
	   case pt_setgray:
	      if (sp >= 1) {
		 fore=stack[--sp].u.val*255;
		 fore *= 0x010101;
	      }
	      break;
	   case pt_setrgbcolor:
	      if (sp >= 3) {
		 fore=(((int) (stack[sp-3].u.val*255)) << 16) +
		    (((int) (stack[sp-2].u.val*255)) << 8) +
		    (int) (stack[sp-1].u.val*255);
		 sp -= 3;
	      }
	      break;
	   case pt_currenthsbcolor:
	   case pt_currentrgbcolor:
	      if (sp+2<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=stack[sp+1].type=stack[sp+2].type =
		    ps_num;
		 if (tok==pt_currentrgbcolor) {
		    stack[sp++].u.val=((fore >> 16)&0xff)/255.;
		    stack[sp++].u.val=((fore >> 8)&0xff)/255.;
		    stack[sp++].u.val=(fore&0xff)/255.;
		 } else {
		    int r=fore >> 16, g=(fore >> 8)&0xff, bl =
		       fore&0xff;
		    int mx, mn;
		    double h, s, b;

		    mx=mn=r;
		    if (mx>g)
		       mn=g;
		    else
		       mx=g;
		    if (mx<bl)
		       mx=bl;
		    if (mn>bl)
		       mn=bl;
		    b=mx/255.;
		    s=h=0;
		    if (mx>0)
		       s=((double) (mx-mn))/mx;
		    if (s != 0) {
		       double rdiff=((double) (mx-r))/(mx-mn);
		       double gdiff=((double) (mx-g))/(mx-mn);
		       double bdiff=((double) (mx-bl))/(mx-mn);

		       if (rdiff==0)
			  h=bdiff-gdiff;
		       else if (gdiff==0)
			  h=2+rdiff-bdiff;
		       else
			  h=4+gdiff-rdiff;
		       h /= 6;
		       if (h<0)
			  h += 1;
		    }
		    stack[sp++].u.val=h;
		    stack[sp++].u.val=s;
		    stack[sp++].u.val=b;
		 }
	      }
	      break;
	   case pt_sethsbcolor:
	      if (sp >= 3) {
		 double h=stack[sp-3].u.val, s=stack[sp-2].u.val, b =
		    stack[sp-1].u.val;
		 int r, g, bl;

		 if (s==0)	/* it's grey */
		    fore=((int) (b*255))*0x010101;
		 else {
		    double sextant=(h-floor(h))*6;
		    double mod=sextant-floor(sextant);
		    double p=b * (1-s), q=b * (1-s * mod), t =
		       b * (1-s * (1-mod));
		    switch ((int) sextant) {
		      case 0:
			 r=b*255.;
			 g=t*255.;
			 bl=p*255.;
			 break;
		      case 1:
			 r=q*255.;
			 g=b*255.;
			 bl=p*255.;
			 break;
		      case 2:
			 r=p*255.;
			 g=b*255.;
			 bl=t*255.;
			 break;
		      case 3:
			 r=p*255.;
			 g=q*255.;
			 bl=b*255.;
			 break;
		      case 4:
			 r=t*255.;
			 g=p*255.;
			 bl=b*255.;
			 break;
		      case 5:
			 r=b*255.;
			 g=p*255.;
			 bl=q*255.;
			 break;
		    }
		    fore=COLOR_CREATE(r, g, bl);
		 }
		 sp -= 3;
	      }
	      break;
	   case pt_currentcmykcolor:
	      if (sp+3<sizeof(stack)/sizeof(stack[0])) {
		 double c, m, y, k;

		 stack[sp].type=stack[sp+1].type=stack[sp+2].type =
		    stack[sp+3].type=ps_num;
		 y=1.-(fore&0xff)/255.;
		 m=1.-((fore >> 8)&0xff)/255.;
		 c=1.-((fore >> 16)&0xff)/255.;
		 k=y;
		 if (k>m)
		    k=m;
		 if (k>c)
		    k=c;
		 if (k != 1) {
		    y=(y-k)/(1-k);
		    m=(m-k)/(1-k);
		    c=(c-k)/(1-k);
		 } else
		    y=m=c=0;
		 stack[sp++].u.val=c;
		 stack[sp++].u.val=m;
		 stack[sp++].u.val=y;
		 stack[sp++].u.val=k;
	      }
	      break;
	   case pt_setcmykcolor:
	      if (sp >= 4) {
		 double c=stack[sp-4].u.val, m=stack[sp-3].u.val, y =
		    stack[sp-2].u.val, k=stack[sp-1].u.val;
		 sp -= 4;
		 if (k==1)
		    fore=0x000000;
		 else {
		    if ((y=(1-k)*y+k)<0)
		       y=0;
		    else if (y>1)
		       y=1;
		    if ((m=(1-k)*m+k)<0)
		       m=0;
		    else if (m>1)
		       m=1;
		    if ((c=(1-k)*c+k)<0)
		       c=0;
		    else if (c>1)
		       c=1;
		    fore=((int) ((1-c)*255.) << 16) |
		       ((int) ((1-m)*255.) << 8) |
		       ((int) ((1-y)*255.));
		 }
	      }
	      break;
	   case pt_currentpoint:
	      if (sp+1<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=current.x;
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=current.y;
	      }
	      break;
	   case pt_fill:
	   case pt_stroke:
	      if (head==NULL && ec->splines != NULL) {
		 /* assume they did a "gsave fill grestore stroke" (or reverse) */
		 ent=ec->splines;
		 if (tok==pt_stroke) {
		    ent->u.splines.cap=linecap;
		    ent->u.splines.join=linejoin;
		    ent->u.splines.stroke_width=linewidth;
		    memcpy(ent->u.splines.transform, transform,
			   sizeof(transform));
		 }
	      } else {
		 ent =
		    EntityCreate(head, linecap, linejoin, linewidth,
				 transform, clippath);
		 ent->next=ec->splines;
		 ec->splines=ent;
	      }
	      if (tok==pt_fill)
		 ent->u.splines.fill.col=fore;
	      else
		 ent->u.splines.stroke.col=fore;
	      head=NULL;
	      cur=NULL;
	      break;
	   case pt_clip:
	      /* I really should intersect the old clip path with the new, but */
	      /*  I don't trust my intersect routine, crashes too often */
	      SplinePointListsFree(clippath);
	      clippath=SplinePointListCopy(head);
	      if (clippath != NULL && clippath->first != clippath->last) {
		 SplineMake3(clippath->last, clippath->first);
		 clippath->last=clippath->first;
	      }
	      break;
	   case pt_imagemask:
	      i=PSAddImagemask(ec, stack, sp, transform, fore);
	      while (sp>i) {
		 --sp;
		 if (stack[sp].type==ps_string || stack[sp].type==ps_instr
		     || stack[sp].type==ps_lit)
		    free(stack[sp].u.str);
		 else if (stack[sp].type==ps_array
			  || stack[sp].type==ps_dict)
		    dictfree(&stack[sp].u.dict);
	      }
	      break;

	      /* We don't do these right, but at least we'll avoid some errors with this hack */
	   case pt_save:
	   case pt_currentmatrix:
	      /* push some junk on the stack */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=0;
	      }
	      /* Fall through into gsave */ ;
	   case pt_gsave:
	      if (gsp<30) {
		 memcpy(gsaves[gsp].transform, transform, sizeof(transform));
		 gsaves[gsp].current=current;
		 gsaves[gsp].linewidth=linewidth;
		 gsaves[gsp].linecap=linecap;
		 gsaves[gsp].linejoin=linejoin;
		 gsaves[gsp].fore=fore;
		 gsaves[gsp].clippath=SplinePointListCopy(clippath);
		 ++gsp;
		 /* I should be saving the "current path" too, but that's too hard */
	      }
	      break;
	   case pt_restore:
	   case pt_setmatrix:
	      /* pop some junk off the stack */
	      if (sp >= 1)
		 --sp;
	      /* Fall through into grestore */ ;
	   case pt_grestore:
	      if (gsp>0) {
		 --gsp;
		 memcpy(transform, gsaves[gsp].transform, sizeof(transform));
		 current=gsaves[gsp].current;
		 linewidth=gsaves[gsp].linewidth;
		 linecap=gsaves[gsp].linecap;
		 linejoin=gsaves[gsp].linejoin;
		 fore=gsaves[gsp].fore;
		 SplinePointListsFree(clippath);
		 clippath=gsaves[gsp].clippath;
	      }
	      break;
	   case pt_null:
	      /* push a 0. I don't handle pointers properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=0;
		 stack[sp++].type=ps_num;
	      }
	      break;
	   case pt_currentoverprint:
	      /* push false. I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=0;
		 stack[sp++].type=ps_bool;
	      }
	      break;
	   case pt_setoverprint:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentflat:
	      /* push 1.0 (default value). I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=1.0;
		 stack[sp++].type=ps_num;
	      }
	      break;
	   case pt_setflat:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentmiterlimit:
	      /* push 10.0 (default value). I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=10.0;
		 stack[sp++].type=ps_num;
	      }
	      break;
	   case pt_setmiterlimit:
	      /* pop one item off stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentpacking:
	      /* push false (default value). I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=0;
		 stack[sp++].type=ps_bool;
	      }
	      break;
	   case pt_setpacking:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentstrokeadjust:
	      /* push false (default value). I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=0;
		 stack[sp++].type=ps_bool;
	      }
	      break;
	   case pt_setstrokeadjust:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentsmoothness:
	      /* default value is installation dependant. I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=1.0;
		 stack[sp++].type=ps_num;
	      }
	      break;
	   case pt_setsmoothness:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentobjectformat:
	      /* default value is installation dependant. I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=0.0;
		 stack[sp++].type=ps_num;
	      }
	      break;
	   case pt_setobjectformat:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;
	   case pt_currentglobal:
	   case pt_currentshared:
	      /* push false (default value). I don't handle this properly */
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].u.val=0;
		 stack[sp++].type=ps_bool;
	      }
	      break;
	   case pt_setglobal:
	      /* pop one item on stack */
	      if (sp >= 1)
		 --sp;
	      break;

	   case pt_openarray:
	   case pt_mark:
	      if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp++].type=ps_mark;
	      }
	      break;
	   case pt_counttomark:
	      for (i=0; i<sp; ++i)
		 if (stack[sp-1-i].type==ps_mark)
		    break;
	      if (i==sp)
		 ErrorMsg(2,"No mark in counttomark\n");
	      else if (sp<sizeof(stack)/sizeof(stack[0])) {
		 stack[sp].type=ps_num;
		 stack[sp++].u.val=i;
	      }
	      break;
	   case pt_cleartomark:
	      for (i=0; i<sp; ++i)
		 if (stack[sp-1-i].type==ps_mark)
		    break;
	      if (i==sp)
		 ErrorMsg(2,"No mark in cleartomark\n");
	      else {
		 while (sp >= i) {
		    --sp;
		    if (stack[sp].type==ps_string
			|| stack[sp].type==ps_instr
			|| stack[sp].type==ps_lit)
		       free(stack[sp].u.str);
		    else if (stack[sp].type==ps_array
			     || stack[sp].type==ps_dict)
		       dictfree(&stack[sp].u.dict);
		 }
	      }
	      break;
	   case pt_closearray:
	      for (i=0; i<sp; ++i)
		 if (stack[sp-1-i].type==ps_mark)
		    break;
	      if (i==sp)
		 ErrorMsg(2,"No mark in ] (close array)\n");
	      else {
		 struct pskeydict dict;

		 dict.cnt=dict.max=i;
		 dict.entries=calloc(i, sizeof(struct pskeyval));
		 for (j=0; j<i; ++j) {
		    dict.entries[j].type=stack[sp-i+j].type;
		    dict.entries[j].u=stack[sp-i+j].u;
		    /* don't need to copy because the things on the stack */
		    /*  are being popped (don't need to free either) */
		 }
		 collectgarbage(&tofrees, &dict);
		 sp=sp-i;
		 stack[sp-1].type=ps_array;
		 stack[sp-1].u.dict=dict;
	      }
	      break;
	   case pt_array:
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 struct pskeydict dict;

		 dict.cnt=dict.max=stack[sp-1].u.val;
		 dict.entries=calloc(dict.cnt, sizeof(struct pskeyval));
		 /* all entries are inited to void */
		 stack[sp-1].type=ps_array;
		 stack[sp-1].u.dict=dict;
	      }
	      break;
	   case pt_aload:
	      sp =
		 aload(sp, stack, sizeof(stack)/sizeof(stack[0]), &tofrees);
	      break;
	   case pt_astore:
	      if (sp >= 1 && stack[sp-1].type==ps_array) {
		 struct pskeydict dict;

		 --sp;
		 dict=stack[sp].u.dict;
		 if (sp >= dict.cnt) {
		    for (i=dict.cnt-1; i >= 0; --i) {
		       --sp;
		       dict.entries[i].type=stack[sp].type;
		       dict.entries[i].u=stack[sp].u;
		    }
		 }
		 stack[sp].type=ps_array;
		 stack[sp].u.dict=dict;
		 ++sp;
	      }
	      break;

	   case pt_output:
	   case pt_outputd:
	   case pt_print:
	      if (sp >= 1) {
		 --sp;
		 switch (stack[sp].type) {
		   case ps_num:
		      printf("%g", (double) stack[sp].u.val);
		      break;
		   case ps_bool:
		      printf("%s", stack[sp].u.tf?"true":"false");
		      break;
		   case ps_string:
		   case ps_instr:
		   case ps_lit:
		      if (tok==pt_outputd)
			 printf(stack[sp].type==ps_lit?"/" :
				stack[sp].type==ps_string?"(":"{");
		      printf("%s", stack[sp].u.str);
		      if (tok==pt_outputd)
			 printf(stack[sp].type==ps_lit?"" :
				stack[sp].type==ps_string?")":"}");
		      free(stack[sp].u.str);
		      break;
		   case ps_void:
		      printf("-- void --");
		      break;
		   case ps_array:
		      if (tok==pt_outputd) {
			 printarray(&stack[sp].u.dict);
			 dictfree(&stack[sp].u.dict);
			 break;
		      }		/* else fall through */
		      dictfree(&stack[sp].u.dict);
		   default:
		      printf("-- nostringval --");
		      break;
		 }
		 if (tok==pt_output || tok==pt_outputd)
		    printf("\n");
	      } else
		 ErrorMsg(2,"Nothing on stack to print\n");
	      break;

	   case pt_cvi:
	   case pt_cvr:
	      /* I shan't distinguish between integers and reals */
	      if (sp >= 1 && stack[sp-1].type==ps_string) {
		 double val=strtod(stack[sp-1].u.str, NULL);

		 free(stack[sp-1].u.str);
		 stack[sp-1].u.val=val;
		 stack[sp-1].type=ps_num;
	      }
	      break;
	   case pt_cvlit:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_array)
		    stack[sp-1].u.dict.is_executable=false;
	      }
	   case pt_cvn:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_string)
		    stack[sp-1].type=ps_lit;
	      }
	   case pt_cvx:
	      if (sp >= 1) {
		 if (stack[sp-1].type==ps_array)
		    stack[sp-1].u.dict.is_executable=true;
	      }
	      break;
	   case pt_cvrs:
	      if (sp >= 3 && stack[sp-1].type==ps_string &&
		  stack[sp-2].type==ps_num &&
		  stack[sp-3].type==ps_num) {
		 if (stack[sp-2].u.val==8)
		    sprintf(stack[sp-1].u.str, "%o",
			    (int) stack[sp-3].u.val);
		 else if (stack[sp-2].u.val==16)
		    sprintf(stack[sp-1].u.str, "%X",
			    (int) stack[sp-3].u.val);
		 else		/* default to radix 10 no matter what they asked for */
		    sprintf(stack[sp-1].u.str, "%g",
			    (double) stack[sp-3].u.val);
		 stack[sp-3]=stack[sp-1];
		 sp -= 2;
	      }
	      break;
	   case pt_cvs:
	      if (sp >= 2 && stack[sp-1].type==ps_string) {
		 switch (stack[sp].type) {
		   case ps_num:
		      sprintf(stack[sp-1].u.str, "%g",
			      (double) stack[sp-2].u.val);
		      break;
		   case ps_bool:
		      sprintf(stack[sp-1].u.str, "%s",
			      stack[sp-2].u.tf?"true":"false");
		      break;
		   case ps_string:
		   case ps_instr:
		   case ps_lit:
		      sprintf(stack[sp-1].u.str, "%s", stack[sp-2].u.str);
		      free(stack[sp].u.str);
		      break;
		   case ps_void:
		      printf("-- void --");
		      break;
		   case ps_array:
		      dictfree(&stack[sp].u.dict);
		   default:
		      sprintf(stack[sp-1].u.str, "-- nostringval --");
		      break;
		 }
		 stack[sp-2]=stack[sp-1];
		 --sp;
	      }
	      break;
	   case pt_stringop:	/* the string keyword, not the () thingy */
	      if (sp >= 1 && stack[sp-1].type==ps_num) {
		 stack[sp-1].type=ps_string;
		 stack[sp-1].u.str=calloc(stack[sp-1].u.val+1, 1);
	      }
	      break;

	   case pt_unknown:
	      if (!warned) {
		 ErrorMsg(2,"Warning: Unable to parse token %s, some features may be lost\n",
			  tokbuf);
		 warned=true;
	      }
	      break;

	   default:
	      break;
	 }
      }
   }
 done:
   if (rs != NULL) {
      int i, cnt, j;

      for (i=sp-1; i >= 0; --i)
	 if (stack[i].type != ps_num)
	    break;
      cnt=sp-1-i;
      if (cnt>rs->max)
	 cnt=rs->max;
      rs->cnt=cnt;
      for (j=i+1; j<sp; ++j)
	 rs->stack[j-i-1]=stack[j].u.val;
   }
   freestuff(stack, sp, &dict, &gb, &tofrees);
   if (head != NULL) {
      ent =
	 EntityCreate(head, linecap, linejoin, linewidth, transform,
		      clippath);
      ent->next=ec->splines;
      ec->splines=ent;
   }
   while (gsp>0) {
      --gsp;
      SplinePointListsFree(gsaves[gsp].clippath);
   }
   SplinePointListsFree(clippath);
   ECCategorizePoints(ec);
   if (ec->width==UNDEFINED_WIDTH)
      ec->width=wrapper->advance_width;
   free(tokbuf);
}

static void InterpretPS(AFILE *ps,char *psstr,EntityChar *ec,
			RetStack * rs) {
   IO wrapper;

   memset(&wrapper, 0, sizeof(wrapper));
   wrapper.advance_width=UNDEFINED_WIDTH;
   pushio(&wrapper, ps, psstr, 0);
   _InterpretPS(&wrapper, ec, rs);
}

static SplinePointList *EraseStroke(SplineChar *sc,SplinePointList *head,
				    SplinePointList * erase) {
   SplineSet *spl, *last;
   SplinePoint *sp;

   if (head==NULL) {
      /* Pointless, but legal */
      SplinePointListsFree(erase);
      return (NULL);
   }

   last=NULL;
   for (spl=head; spl != NULL; spl=spl->next) {
      for (sp=spl->first; sp != NULL;) {
	 sp->selected=false;
	 if (sp->next==NULL)
	    break;
	 sp=sp->next->to;
	 if (sp==spl->first)
	    break;
      }
      last=spl;
   }
   for (spl=erase; spl != NULL; spl=spl->next) {
      for (sp=spl->first; sp != NULL;) {
	 sp->selected=true;
	 if (sp->next==NULL)
	    break;
	 sp=sp->next->to;
	 if (sp==spl->first)
	    break;
      }
   }
   last->next=erase;
   return (SplineSetRemoveOverlap(sc, head, over_exclude));
}

static Entity *EntityReverse(Entity *ent) {
   Entity *next, *last=NULL;

   while (ent != NULL) {
      next=ent->next;
      ent->next=last;
      last=ent;
      ent=next;
   }
   return (last);
}

static void EntityCharCorrectDir(EntityChar *ec) {
   SplineSet *ss;
   Entity *ent;
   int changed;

   for (ent=ec->splines; ent != NULL; ent=ent->next) {
      /* ignore splines which are only stoked, but not filled */
      if (ent->type==et_splines && ent->u.splines.fill.col != 0xffffffff) {
	 /* Correct the direction of each stroke or fill with respect to */
	 /*  the splines in it */
	 SplineSetsCorrect(ent->u.splines.splines, &changed);
	 if (ent->u.splines.fill.col==0xffffff) {
	    /* If they are filling with white, then assume they mean */
	    /*  an internal area that should be drawn backwards */
	    for (ss=ent->u.splines.splines; ss != NULL; ss=ss->next)
	       SplineSetReverse(ss);
	 }
	 SplineSetsCorrect(ent->clippath, &changed);
      }
   }
}

void EntityDefaultStrokeFill(Entity * ent) {
   while (ent != NULL) {
      if (ent->type==et_splines &&
	  ent->u.splines.stroke.col==0xffffffff &&
	  ent->u.splines.fill.col==0xffffffff) {
	 SplineSet *spl;
	 int all=1;

	 for (spl=ent->u.splines.splines; spl != NULL; spl=spl->next)
	    if (spl->first->prev != NULL) {
	       all=false;
	       break;
	    }
	 if (all && ent->u.splines.splines != NULL &&
	     (ent->u.splines.stroke_width==0
	      || ent->u.splines.stroke_width==WIDTH_INHERITED))
	    ent->u.splines.stroke_width=40;	/* random guess */
	 if (ent->u.splines.stroke_width==0
	     || ent->u.splines.stroke_width==WIDTH_INHERITED)
	    ent->u.splines.fill.col=COLOR_INHERITED;
	 else
	    ent->u.splines.stroke.col=COLOR_INHERITED;
      }
      ent=ent->next;
   }
}

SplinePointList *SplinesFromEntityChar(EntityChar * ec, int *flags,
				       int is_stroked) {
   Entity *ent, *next;
   SplinePointList *head=NULL, *last, *new, *nlast, *temp, *each, *transed;
   StrokeInfo si;
   double inversetrans[6];

   /*SplineSet *spl; */
   int handle_eraser=false;
   int ask=false;

   EntityDefaultStrokeFill(ec->splines);

   if (!is_stroked) {

      if (*flags==-1) {
	 for (ent=ec->splines; ent != NULL; ent=ent->next) {
	    if (ent->type==et_splines &&
		(ent->u.splines.fill.col==0xffffff ||
		 /*ent->u.splines.clippath!=NULL || */
		 (ent->u.splines.stroke_width != 0
		  && ent->u.splines.stroke.col != 0xffffffff))) {
	       ask=true;
	       break;
	    }
	 }
	 if (ask)
	    *flags=sf_correctdir;
      }

      if (*flags&sf_correctdir)	/* Will happen if flags still unset (-1) */
	 EntityCharCorrectDir(ec);

      handle_eraser=*flags != -1 && (*flags&sf_handle_eraser);
      if (handle_eraser)
	 ec->splines=EntityReverse(ec->splines);
   }

   for (ent=ec->splines; ent != NULL; ent=next) {
      next=ent->next;
      if (ent->type==et_splines && is_stroked) {
	 if (head==NULL)
	    head=ent->u.splines.splines;
	 else
	    last->next=ent->u.splines.splines;
	 if (ent->u.splines.splines != NULL)
	    for (last=ent->u.splines.splines; last->next != NULL;
		 last=last->next);
	 ent->u.splines.splines=NULL;
      } else if (ent->type==et_splines) {
	 if (ent->u.splines.stroke.col != 0xffffffff &&
	     (ent->u.splines.fill.col==0xffffffff
	      || ent->u.splines.stroke_width != 0)) {
	    /* What does a stroke width of 0 mean? PS Says minimal width line */
	    /* How do we implement that? Special case: If filled and stroked 0, then */
	    /*  ignore the stroke. This idiom is used by MetaPost sometimes and means */
	    /*  no stroke */
	    memset(&si, '\0', sizeof(si));
	    si.join=ent->u.splines.join;
	    si.cap=ent->u.splines.cap;
	    /* si.removeoverlapifneeded=*flags&sf_removeoverlap?1:0; */
	    si.radius=ent->u.splines.stroke_width/2;
	    if (ent->u.splines.stroke_width==WIDTH_INHERITED)
	       si.radius=.5;
	    if (si.cap==lc_inherited)
	       si.cap=lc_butt;
	    if (si.join==lc_inherited)
	       si.join=lj_miter;
	    new=NULL;
	    MatInverse(inversetrans, ent->u.splines.transform);
	    transed =
	       SplinePointListTransform(SplinePointListCopy
					(ent->u.splines.splines),
					inversetrans, tpt_AllPoints);
	    for (each=transed; each != NULL; each=each->next) {
	       temp=SplineSetStroke(each, &si, false);
	       if (new==NULL)
		  new=temp;
	       else
		  nlast->next=temp;
	       if (temp != NULL)
		  for (nlast=temp; nlast->next != NULL;
		       nlast=nlast->next);
	    }
	    new =
	       SplinePointListTransform(new, ent->u.splines.transform,
					tpt_AllPoints);
	    SplinePointListsFree(transed);
	    if (handle_eraser && ent->u.splines.stroke.col==0xffffff) {
	       head=EraseStroke(ec->sc, head, new);
	       last=head;
	       if (last != NULL)
		  for (; last->next != NULL; last=last->next);
	    } else {
	       if (head==NULL)
		  head=new;
	       else
		  last->next=new;
	       if (new != NULL)
		  for (last=new; last->next != NULL; last=last->next);
	    }
	 }
	 /* If they have neither a stroke nor a fill, pretend they said fill */
	 if (ent->u.splines.fill.col==0xffffffff
	     && ent->u.splines.stroke.col != 0xffffffff)
	    SplinePointListsFree(ent->u.splines.splines);
	 else if (handle_eraser && ent->u.splines.fill.col==0xffffff) {
	    head=EraseStroke(ec->sc, head, ent->u.splines.splines);
	    last=head;
	    if (last != NULL)
	       for (; last->next != NULL; last=last->next);
	 } else {
	    new=ent->u.splines.splines;
	    if (head==NULL)
	       head=new;
	    else
	       last->next=new;
	    if (new != NULL)
	       for (last=new; last->next != NULL; last=last->next);
	 }
      }
      SplinePointListsFree(ent->clippath);
      free(ent);
   }
   return (head);
}

SplinePointList *SplinesFromEntities(Entity * ent, int *flags, int is_stroked) {
   EntityChar ec;

   memset(&ec, '\0', sizeof(ec));
   ec.splines=ent;
   return (SplinesFromEntityChar(&ec, flags, is_stroked));
}

SplinePointList *SplinePointListInterpretPS(AFILE *ps, int flags,
					    int is_stroked, int *width) {
   EntityChar ec;
   SplineChar sc;

   memset(&ec, '\0', sizeof(ec));
   ec.width=ec.vwidth=UNDEFINED_WIDTH;
   memset(&sc, 0, sizeof(sc));
   sc.name="<No particular character>";
   ec.sc=&sc;
   InterpretPS(ps, NULL, &ec, NULL);
   if (width != NULL)
      *width=ec.width;
   return (SplinesFromEntityChar(&ec, &flags, is_stroked));
}

Entity *EntityInterpretPS(AFILE *ps, int *width) {
   EntityChar ec;

   memset(&ec, '\0', sizeof(ec));
   ec.width=ec.vwidth=UNDEFINED_WIDTH;
   InterpretPS(ps, NULL, &ec, NULL);
   if (width != NULL)
      *width=ec.width;
   return (ec.splines);
}

static RefChar *revrefs(RefChar *cur) {
   RefChar *p, *n;

   if (cur==NULL)
      return (NULL);

   p=NULL;
   for (; (n=cur->next) != NULL; cur=n) {
      cur->next=p;
      p=cur;
   }
   cur->next=p;
   return (cur);
}

static void SCInterpretPS(AFILE *ps,SplineChar *sc,int *flags) {
   EntityChar ec;
   double dval;
   char tokbuf[10];
   IO wrapper;
   int ch;

   while (isspace(ch=agetc(ps)));
   aungetc(ch, ps);

   memset(&wrapper, 0, sizeof(wrapper));
   wrapper.advance_width=UNDEFINED_WIDTH;
   if (ch != '<') {
      pushio(&wrapper, ps, NULL, 0);

      if (nextpstoken(&wrapper, &dval, tokbuf, sizeof(tokbuf)) !=
	  pt_opencurly)
	 ErrorMsg(2,"We don't understand this font\n");
   } else {
      (void) agetc(ps);
      pushfogio(&wrapper, ps);
   }
   memset(&ec, '\0', sizeof(ec));
   ec.fromtype3=true;
   ec.sc=sc;
   _InterpretPS(&wrapper, &ec, NULL);
   sc->width=ec.width;
   sc->layer_cnt=1;
   SCAppendEntityLayers(sc, ec.splines);
   if (sc->layer_cnt==1)
      ++sc->layer_cnt;
   sc->layers[ly_fore].refs=revrefs(ec.refs);
   free(wrapper.top);
}

void PSFontInterpretPS(AFILE *ps, struct charprocs *cp, char **encoding) {
   char tokbuf[100];
   int tok, i, j;
   double dval;
   SplineChar *sc;
   EntityChar dummy;
   RefChar *p, *ref, *next;
   IO wrapper;
   int flags=-1;

   wrapper.top=NULL;
   wrapper.advance_width=UNDEFINED_WIDTH;
   pushio(&wrapper, ps, NULL, 0);

   while ((tok =
	   nextpstoken(&wrapper, &dval, tokbuf, sizeof(tokbuf))) != pt_eof
	  && tok != pt_end) {
      if (tok==pt_namelit) {
	 if (cp->next >= cp->cnt) {
	    ++cp->cnt;
	    cp->keys=realloc(cp->keys, cp->cnt * sizeof(char *));
	    cp->values=realloc(cp->values, cp->cnt * sizeof(char *));
	 }
	 if (cp->next<cp->cnt) {
	    sc=SplineCharCreate(2);
	    cp->keys[cp->next]=fastrdup(tokbuf);
	    cp->values[cp->next++]=sc;
	    sc->name=fastrdup(tokbuf);
	    SCInterpretPS(ps, sc, &flags);
	 } else {
	    memset(&dummy, 0, sizeof(dummy));
	    dummy.fromtype3=true;
	    InterpretPS(ps, NULL, &dummy, NULL);
	 }
      }
   }
   free(wrapper.top);

   /* References were done by name in the postscript. we stored the names in */
   /*  ref->sc (which is a hack). Now look up all those names and replace */
   /*  with the appropriate splinechar. If we can't find anything then throw */
   /*  out the reference */
   /* Further fixups come later, where all ps refs are fixedup */
   for (i=0; i<cp->next; ++i) {
      for (p=NULL, ref=cp->values[i]->layers[ly_fore].refs; ref != NULL;
	   ref=next) {
	 char *refname=(char *) (ref->sc);

	 next=ref->next;
	 if (ref->sc==NULL)
	    refname=encoding[ref->orig_pos];
	 for (j=0; j<cp->next; ++j)
	    if (strcmp(cp->keys[j], refname)==0)
	       break;
	 free(ref->sc);		/* a string, not a splinechar */
	 if (j != cp->next) {
	    ref->sc=cp->values[j];
	    SCMakeDependent(cp->values[i], ref->sc);
	    ref->adobe_enc=getAdobeEnc(ref->sc->name);
	    ref->checked=true;
	    p=ref;
	 } else {
	    if (p==NULL)
	       cp->values[i]->layers[ly_fore].refs=next;
	    else
	       p->next=next;
	    ref->next=NULL;
	    RefCharFree(ref);
	 }
      }
   }
}

/* Slurp up an encoding in the form:
 /Enc-name [
    /charname
    ...
 ] def
We're not smart here no: 0 1 255 {1 index exch /.notdef put} for */
Encoding *PSSlurpEncodings(AFILE *file) {
   char *names[1024];
   int32_t encs[1024];
   Encoding *item, *head=NULL, *last;
   char *encname;
   char tokbuf[200];
   IO wrapper;
   double dval;
   int i, max, any, enc, codepointsonly;
   int tok;

   wrapper.top=NULL;
   wrapper.advance_width=UNDEFINED_WIDTH;
   pushio(&wrapper, file, NULL, 0);

   while ((tok =
	   nextpstoken(&wrapper, &dval, tokbuf, sizeof(tokbuf))) != pt_eof) {
      encname=NULL;
      if (tok==pt_namelit) {
	 encname=fastrdup(tokbuf);
	 tok=nextpstoken(&wrapper, &dval, tokbuf, sizeof(tokbuf));
      }
      if (tok != pt_openarray && tok != pt_opencurly)
	 return (head);
      for (i=0; i<sizeof(names)/sizeof(names[0]); ++i) {
	 encs[i]=-1;
	 names[i]=NULL;
      }
      codepointsonly=CheckCodePointsComment(&wrapper);

      max=-1;
      any=0;
      i=0;
      while ((tok =
	      nextpstoken(&wrapper, &dval, tokbuf, sizeof(tokbuf))) != pt_eof
	     && tok != pt_closearray && tok != pt_closecurly) {
	 if (tok==pt_namelit && i<sizeof(names)/sizeof(names[0])) {
	    max=i;
	    if (strcmp(tokbuf, ".notdef")==0) {
	       encs[i]=-1;
	    } else if ((enc=UniFromName(tokbuf, ui_none, &custom)) != -1) {
	       encs[i]=enc;
	       /* Used not to do this, but there are several legal names */
	       /*  for some slots and people get unhappy (rightly) if we */
	       /*  use the wrong one */
	       names[i]=fastrdup(tokbuf);
	       any=1;
	    } else {
	       names[i]=fastrdup(tokbuf);
	       any=1;
	    }
	 }
	 ++i;
      }
      if (encname != NULL) {
	 tok=nextpstoken(&wrapper, &dval, tokbuf, sizeof(tokbuf));
	 if (tok==pt_def) {
	    /* Good */
	    ;
	 } else {
	    /* TODO! */
	    /* I guess it's not good... */
	    ;
	 }
      }
      if (max != -1) {
	 if (++max<256)
	    max=256;
	 item=calloc(1, sizeof(Encoding));
	 item->enc_name=encname;
	 item->char_cnt=max;
	 item->unicode=malloc(max * sizeof(int32_t));
	 memcpy(item->unicode, encs, max * sizeof(int32_t));
	 if (any && !codepointsonly) {
	    item->psnames=calloc(max, sizeof(char *));
	    memcpy(item->psnames, names, max * sizeof(char *));
	 } else {
	    for (i=0; i<max; ++i)
	       free(names[i]);
	 }
	 if (head==NULL)
	    head=item;
	 else
	    last->next=item;
	 last=item;
      }
   }

   return (head);
}

static void closepath(SplinePointList *cur,int is_type2) {
   if (cur != NULL && cur->first==cur->last && cur->first->prev==NULL
       && is_type2)
      return;			/* The "path" is just a single point created by a moveto */
   /* Probably we're just doing another moveto */
   if (cur != NULL && cur->first != NULL && cur->first != cur->last) {
/* I allow for greater errors here than I do in the straight postscript code */
/*  because: 1) the rel-rel operators will accumulate more rounding errors   */
/*  2) I only output 2 decimal digits after the decimal in type1 output */
      if (RealWithin(cur->first->me.x, cur->last->me.x, .05)
	  && RealWithin(cur->first->me.y, cur->last->me.y, .05)) {
	 SplinePoint *oldlast=cur->last;

	 cur->first->prevcp=oldlast->prevcp;
	 cur->first->prevcp.x += (cur->first->me.x-oldlast->me.x);
	 cur->first->prevcp.y += (cur->first->me.y-oldlast->me.y);
	 cur->first->noprevcp=oldlast->noprevcp;
	 oldlast->prev->from->next=NULL;
	 cur->last=oldlast->prev->from;
	 chunkfree(oldlast->prev, sizeof(*oldlast));
	 chunkfree(oldlast->hintmask, sizeof(HintMask));
	 chunkfree(oldlast, sizeof(*oldlast));
      }
      CheckMake(cur->last, cur->first);
      SplineMake3(cur->last, cur->first);
      cur->last=cur->first;
   }
}

static void UnblendFree(StemInfo *h) {
   while (h != NULL) {
      chunkfree(h->u.unblended, sizeof(double[2][MmMax]));
      h->u.unblended=NULL;
      h=h->next;
   }
}

static StemInfo *HintsAppend(StemInfo *to,StemInfo *extra) {
   StemInfo *h;

   if (to==NULL)
      return (extra);
   if (extra==NULL)
      return (to);
   for (h=to; h->next != NULL; h=h->next);
   h->next=extra;
   return (to);
}

static StemInfo *HintNew(double start,double width) {
   StemInfo *h;

   h=chunkalloc(sizeof(StemInfo));
   h->start=start;
   h->width=width;
   return (h);
}

static void RemapHintMask(HintMask *hm,int mapping[96],int max) {
   HintMask rpl;
   int i, mb;

   if (hm==NULL)
      return;

   if (max>96)
      max=96;
   mb=(max+7) >> 3;

   memset(&rpl, 0, mb);
   for (i=0; i<max; ++i)
      if ((*hm)[i >> 3]&(0x80 >> (i&0x7)))
	 rpl[mapping[i] >> 3] |= (0x80 >> (mapping[i]&0x7));
   memcpy(hm, &rpl, mb);
}

static void HintsRenumber(SplineChar *sc) {
   /* In a type1 font the hints may get added to our hint list in a semi- */
   /*  random order. In an incorrect type2 font the same thing could happen. */
   /*  Force the order to be correct, and then update all masks */
   int mapping[96];
   int i, max;
   StemInfo *h;
   SplineSet *spl;
   SplinePoint *sp;

   for (i=0; i<96; ++i)
      mapping[i]=i;

   i=0;
   for (h=sc->hstem; h != NULL; h=h->next) {
      if (h->hintnumber<96 && i<96) {
	 mapping[h->hintnumber]=i;
	 h->hintnumber=i++;
      }
      chunkfree(h->u.unblended, sizeof(double[2][MmMax]));
      h->u.unblended=NULL;
   }
   for (h=sc->vstem; h != NULL; h=h->next) {
      if (h->hintnumber<96 && i<96) {
	 mapping[h->hintnumber]=i;
	 h->hintnumber=i++;
      }
      chunkfree(h->u.unblended, sizeof(double[2][MmMax]));
      h->u.unblended=NULL;
   }
   max=i;
   for (i=0; i<max; ++i)
      if (mapping[i] != i)
	 break;
   if (i==max)
      return;			/* Didn't change the order */

   for (i=0; i<sc->countermask_cnt; ++i)
      RemapHintMask(&sc->countermasks[i], mapping, max);
   for (spl=sc->layers[ly_fore].splines; spl != NULL; spl=spl->next) {
      for (sp=spl->first;;) {
	 RemapHintMask(sp->hintmask, mapping, max);
	 if (sp->next==NULL)
	    break;
	 sp=sp->next->to;
	 if (sp==spl->first)
	    break;
      }
   }
}

int UnblendedCompare(double u1[MmMax], double u2[MmMax], int cnt) {
   int i;

   for (i=0; i<cnt; ++i) {
      if (u1[i] != u2[i])
	 return (u1[i]>u2[i]?1:-1);
   }
   return (0);
}

static StemInfo *SameH(StemInfo *old,double start,double width,
		       double unblended[2][MmMax], int instance_count) {
   StemInfo *sameh;

   if (instance_count==0) {
      for (sameh=old; sameh != NULL; sameh=sameh->next)
	 if (sameh->start==start && sameh->width==width)
	    break;
   } else {
      int j;

      for (j=1; j<instance_count; ++j) {
	 unblended[0][j] += unblended[0][j-1];
	 unblended[1][j] += unblended[1][j-1];
      }
      for (sameh=old; sameh != NULL; sameh=sameh->next) {
	 if ((*sameh->u.unblended)[0]==NULL
	     || (*sameh->u.unblended)[1]==NULL)
	    continue;
	 if (UnblendedCompare
	     ((*sameh->u.unblended)[0], unblended[0], instance_count)==0
	     && UnblendedCompare((*sameh->u.unblended)[1], unblended[1],
				 instance_count)==0)
	    break;
      }
   }
   return (sameh);
}

static double Blend(double u[MmMax],struct pscontext *context) {
   double sum=u[0];
   int i;

   for (i=1; i<context->instance_count; ++i)
      sum += context->blend_values[i] * u[i];
   return (sum);
}

/* this handles either Type1 or Type2 charstrings. Type2 charstrings have */
/*  more operators than Type1s and the old operators have extended meanings */
/*  (ie. the rlineto operator can produce more than one line). But pretty */
/*  much it's a superset and if we parse for type2 (with a few additions) */
/*  we'll get it right */
/* Char width is done differently. Moveto starts a newpath. 0xff starts a 16.16*/
/*  number rather than a 32 bit number */
SplineChar *PSCharStringToSplines(uint8_t * type1, int len,
				  struct pscontext * context,
				  struct pschars * subrs,
				  struct pschars * gsubrs, const char *name) {
   int is_type2=context->is_type2;
   double stack[50];
   int sp=0, v;		/* Type1 stack is about 25 long, Type2 stack is 48 */
   double transient[32];
   SplineChar *ret=SplineCharCreate(2);
   SplinePointList *cur=NULL, *oldcur=NULL;
   RefChar *r1, *r2, *rlast=NULL;
   DBasePoint current;
   double dx, dy, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6;
   SplinePoint *pt;

   /* subroutines may be nested to a depth of 10 */
   struct substate {
      unsigned char *type1;
      int len;
      int subnum;
   } pcstack[11];
   int pcsp=0;
   StemInfo *hint, *hp;
   double pops[30];
   int popsp=0;
   int base, polarity;
   double coord;
   struct pschars *s;
   int hint_cnt=0;
   StemInfo *activeh=NULL, *activev=NULL, *sameh;
   HintMask *pending_hm=NULL;
   HintMask *counters[96];
   int cp=0;
   double unblended[2][MmMax];
   int last_was_b1=false, old_last_was_b1;

   if (!is_type2 && context->instance_count>1)
      memset(unblended, 0, sizeof(unblended));

   ret->name=fastrdup(name);
   ret->unicodeenc=-1;
   ret->width=(int16_t) 0x8000;
   if (name==NULL)
      name="unnamed";
   ret->manualhints=true;

   current.x=current.y=0;
   while (len>0) {
      if (sp>48) {
	 ErrorMsg(2,"Stack got too big in %s\n", name);
	 sp=48;
      }
      base=0;
      --len;
      if ((v=*type1++) >= 32) {
	 if (v <= 246) {
	    stack[sp++]=v-139;
	 } else if (v <= 250) {
	    stack[sp++]=(v-247)*256+*type1+++108;
	    --len;
	 } else if (v <= 254) {
	    stack[sp++]=-(v-251)*256-*type1++-108;
	    --len;
	 } else {
	    int val =
	       (*type1 << 24)|(type1[1] << 16)|(type1[2] << 8)|type1[3];
	    stack[sp++]=val;
	    type1 += 4;
	    len -= 4;
	    if (is_type2) {
#ifndef PSFixed_Is_TTF		/* The type2 spec is contradictory. It says this is a */
	       /*  two's complement number, but it also says it is a */
	       /*  Fixed, which in truetype is not two's complement */
	       /*  (mantisa is always unsigned) */
	       stack[sp-1] /= 65536.;
#else
	       int mant=val&0xffff;

	       stack[sp-1]=(val >> 16)+mant/65536.;
#endif
	    }
	 }
      } else if (v==28) {
	 stack[sp++]=(short) ((type1[0] << 8)|type1[1]);
	 type1 += 2;
	 len -= 2;
	 /* In the Dict tables of CFF, a 5byte fixed value is prefixed by a */
	 /*  29 code. In Type2 strings the prefix is 255. */
      } else if (v==12) {
	 old_last_was_b1=last_was_b1;
	 last_was_b1=false;
	 v=*type1++;
	 --len;
	 switch (v) {
	   case 0:		/* dotsection */
	      if (is_type2)
		 ErrorMsg(2,"%s\'s dotsection operator is depreciated for Type2\n",
			  name);
	      sp=0;
	      break;
	   case 1:		/* vstem3 */ /* specifies three v hints zones at once */
	      if (sp<6)
		 ErrorMsg(2,"Stack underflow on vstem3 in %s\n", name);
	      /* according to the standard, if there is a vstem3 there can't */
	      /*  be any vstems, so there can't be any confusion about hint order */
	      /*  so we don't need to worry about unblended stuff */
	      if (is_type2)
		 ErrorMsg(2,"%s\'s vstem3 operator is not supported for Type2\n",
			  name);
	      sameh=NULL;
	      if (!is_type2)
		 sameh =
		    SameH(ret->vstem, stack[0]+ret->lsidebearing, stack[1],
			  unblended, 0);
	      hint=HintNew(stack[0]+ret->lsidebearing, stack[1]);
	      hint->hintnumber =
		 sameh != NULL?sameh->hintnumber:hint_cnt++;
	      if (activev==NULL)
		 activev=hp=hint;
	      else {
		 for (hp=activev; hp->next != NULL; hp=hp->next);
		 hp->next=hint;
		 hp=hint;
	      }
	      sameh=NULL;
	      if (!is_type2)
		 sameh =
		    SameH(ret->vstem, stack[2]+ret->lsidebearing, stack[3],
			  unblended, 0);
	      hp->next=HintNew(stack[2]+ret->lsidebearing, stack[3]);
	      hp->next->hintnumber =
		 sameh != NULL?sameh->hintnumber:hint_cnt++;
	      if (!is_type2)
		 sameh =
		    SameH(ret->vstem, stack[4]+ret->lsidebearing, stack[5],
			  unblended, 0);
	      hp->next->next =
		 HintNew(stack[4]+ret->lsidebearing, stack[5]);
	      hp->next->next->hintnumber =
		 sameh != NULL?sameh->hintnumber:hint_cnt++;
	      if (!is_type2 && hp->next->next->hintnumber<96) {
		 if (pending_hm==NULL)
		    pending_hm=chunkalloc(sizeof(HintMask));
		 (*pending_hm)[hint->hintnumber >> 3] |=
		    0x80 >> (hint->hintnumber&0x7);
		 (*pending_hm)[hint->next->hintnumber >> 3] |=
		    0x80 >> (hint->next->hintnumber&0x7);
		 (*pending_hm)[hint->next->next->hintnumber >> 3] |=
		    0x80 >> (hint->next->next->hintnumber&0x7);
	      }
	      hp=hp->next->next;
	      sp=0;
	      break;
	   case 2:		/* hstem3 */ /* specifies three h hints zones at once */
	      if (sp<6)
		 ErrorMsg(2,"Stack underflow on hstem3 in %s\n", name);
	      if (is_type2)
		 ErrorMsg(2,"%s\'s vstem3 operator is not supported for Type2\n",
			  name);
	      sameh=NULL;
	      if (!is_type2)
		 sameh=SameH(ret->hstem, stack[0], stack[1], unblended, 0);
	      hint=HintNew(stack[0], stack[1]);
	      hint->hintnumber =
		 sameh != NULL?sameh->hintnumber:hint_cnt++;
	      if (activeh==NULL)
		 activeh=hp=hint;
	      else {
		 for (hp=activeh; hp->next != NULL; hp=hp->next);
		 hp->next=hint;
		 hp=hint;
	      }
	      sameh=NULL;
	      if (!is_type2)
		 sameh=SameH(ret->hstem, stack[2], stack[3], unblended, 0);
	      hp->next=HintNew(stack[2], stack[3]);
	      hp->next->hintnumber =
		 sameh != NULL?sameh->hintnumber:hint_cnt++;
	      sameh=NULL;
	      if (!is_type2)
		 sameh=SameH(ret->hstem, stack[4], stack[5], unblended, 0);
	      hp->next->next=HintNew(stack[4], stack[5]);
	      hp->next->next->hintnumber =
		 sameh != NULL?sameh->hintnumber:hint_cnt++;
	      if (!is_type2 && hp->next->next->hintnumber<96) {
		 if (pending_hm==NULL)
		    pending_hm=chunkalloc(sizeof(HintMask));
		 (*pending_hm)[hint->hintnumber >> 3] |=
		    0x80 >> (hint->hintnumber&0x7);
		 (*pending_hm)[hint->next->hintnumber >> 3] |=
		    0x80 >> (hint->next->hintnumber&0x7);
		 (*pending_hm)[hint->next->next->hintnumber >> 3] |=
		    0x80 >> (hint->next->next->hintnumber&0x7);
	      }
	      hp=hp->next->next;
	      sp=0;
	      break;
	   case 6:		/* seac */ /* build accented characters */
	    seac:
	      if (sp<5)
		 ErrorMsg(2,"Stack underflow on seac in %s\n", name);
	      if (is_type2) {
		 if (v==6)
		    ErrorMsg(2,"%s\'s SEAC operator is invalid for Type2\n",
			     name);
		 else
		    ErrorMsg(2,"%s\'s SEAC-like endchar operator is depreciated for Type2\n",
			     name);
	      }
	      /* stack[0] must be the lsidebearing of the accent. I'm not sure why */
	      r1=RefCharCreate();
	      r2=RefCharCreate();
	      r2->transform[0]=1;
	      r2->transform[3]=1;
	      r2->transform[4]=stack[1]-(stack[0]-ret->lsidebearing);
	      r2->transform[5]=stack[2];
	      /* the translation of the accent here is said to be relative */
	      /*  to the origins of the base character. I think they place */
	      /*  the origin at the left bearing. And they don't mean the  */
	      /*  base char at all, they mean the current char's lbearing  */
	      /*  (which is normally the same as the base char's, except   */
	      /*  when I has a big accent (like diaerisis) */
	      r1->transform[0]=1;
	      r1->transform[3]=1;
	      r1->adobe_enc=stack[3];
	      r2->adobe_enc=stack[4];
	      if (stack[3]<0 || stack[3] >= 256 || stack[4]<0
		  || stack[4] >= 256) {
		 ErrorMsg(2,"Reference encoding out of bounds in %s\n",
			  name);
		 r1->adobe_enc=0;
		 r2->adobe_enc=0;
	      }
	      r1->next=r2;
	      if (rlast != NULL)
		 rlast->next=r1;
	      else
		 ret->layers[ly_fore].refs=r1;
	      ret->changedsincelasthinted=true;	/* seac glyphs contain no hints */
	      rlast=r2;
	      sp=0;
	      break;
	   case 7:		/* sbw */ /* generalized width/sidebearing command */
	      if (sp<4)
		 ErrorMsg(2,"Stack underflow on sbw in %s\n", name);
	      if (is_type2)
		 ErrorMsg(2,"%s\'s sbw operator is not supported for Type2\n",
			  name);
	      ret->lsidebearing=stack[0];
	      /* stack[1] is lsidebearing y (only for vertical writing styles, CJK) */
	      ret->width=stack[2];
	      /* stack[3] is height (for vertical writing styles, CJK) */
	      sp=0;
	      break;
	   case 5:
	   case 9:
	   case 14:
	   case 26:
	      if (sp<1)
		 ErrorMsg(2,"Stack underflow on unary operator in %s\n",
			  name);
	      switch (v) {
		case 5:
		   stack[sp-1]=(stack[sp-1]==0);
		   break;	/* not */
		case 9:
		   if (stack[sp-1]<0)
		      stack[sp-1]=-stack[sp-1];
		   break;	/* abs */
		case 14:
		   stack[sp-1]=-stack[sp-1];
		   break;	/* neg */
		case 26:
		   stack[sp-1]=sqrt(stack[sp-1]);
		   break;	/* sqrt */
	      }
	      break;
	   case 3:
	   case 4:
	   case 10:
	   case 11:
	   case 12:
	   case 15:
	   case 24:
	      if (sp<2)
		 ErrorMsg(2,"Stack underflow on binary operator in %s\n",
			  name);
	      else
		 switch (v) {
		   case 3:	/* and */
		      stack[sp-2]=(stack[sp-1] != 0
				       && stack[sp-2] != 0);
		      break;
		   case 4:	/* and */
		      stack[sp-2]=(stack[sp-1] != 0
				       || stack[sp-2] != 0);
		      break;
		   case 10:	/* add */
		      stack[sp-2] += stack[sp-1];
		      break;
		   case 11:	/* sub */
		      stack[sp-2] -= stack[sp-1];
		      break;
		   case 12:	/* div */
		      stack[sp-2] /= stack[sp-1];
		      break;
		   case 24:	/* mul */
		      stack[sp-2] *= stack[sp-1];
		      break;
		   case 15:	/* eq */
		      stack[sp-2]=(stack[sp-1]==stack[sp-2]);
		      break;
		 }
	      --sp;
	      break;
	   case 22:		/* ifelse */
	      if (sp<4)
		 ErrorMsg(2,"Stack underflow on ifelse in %s\n", name);
	      else {
		 if (stack[sp-2]>stack[sp-1])
		    stack[sp-4]=stack[sp-3];
		 sp -= 3;
	      }
	      break;
	  case 23:		/* random */
	    /* This function returns something (0,1]. It's not clear to me */
	    /*  if rand includes 0 and RAND_MAX or not, but this approach */
	    /*  should work no matter what */
	    stack[sp]=1.0-sfmt_to_real2(sfmt_genrand_uint32(&fa_sfmt));
	    sp++;
	    break;
	  case 16:		/* callothersubr */
	      /* stack[sp-1] is the number of the thing to call in the othersubr array */
	      /* stack[sp-2] is the number of args to grab off our stack and put on the */
	      /*  real postscript stack */
	      if (is_type2)
		 ErrorMsg(2,"Type2 fonts do not support the Type1 callothersubrs operator\n");
	      if (sp<2 || sp<2+stack[sp-2]) {
		 ErrorMsg(2,"Stack underflow on callothersubr in %s\n",
			  name);
		 sp=0;
	      } else {
		 int tot=stack[sp-2], i, k, j;

		 popsp=0;
		 for (k=sp-3; k >= sp-2-tot; --k)
		    pops[popsp++]=stack[k];
		 /* othersubrs 0-3 must be interpretted. 0-2 are Flex, 3 is Hint Replacement */
		 /* othersubrs 12,13 are for counter hints. We don't need to */
		 /*  do anything to ignore them */
		 /* Subroutines 14-18 are multiple master blenders. We need */
		 /*  to pay attention to them too */
		 switch ((int) stack[sp-1]) {
		   case 3:{
			 /* when we weren't capabable of hint replacement we */
			 /*  punted by putting 3 on the stack (T1 spec page 70) */
			 /*  subroutine 3 is a noop */
			 /*pops[popsp-1]=3; */
			 ret->manualhints=false;
			 /* We can manage hint substitution from hintmask though */
			 /*  well enough that we needn't clear the manualhints bit */
			 ret->hstem=HintsAppend(ret->hstem, activeh);
			 activeh=NULL;
			 ret->vstem=HintsAppend(ret->vstem, activev);
			 activev=NULL;
		      }
		      break;
		   case 1:{
			 /* Essentially what we want to do is draw a line from */
			 /*  where we are at the beginning to where we are at */
			 /*  the end. So we save the beginning here (this starts */
			 /*  the flex sequence), we ignore all calls to othersub */
			 /*  2, and when we get to othersub 0 we put everything */
			 /*  back to where it should be and free up whatever */
			 /*  extranious junk we created along the way and draw */
			 /*  our line. */
			 /* Let's punt a little less, and actually figure out */
			 /*  the appropriate rrcurveto commands and put in a */
			 /*  dished serif */
			 /* We should never get here in a type2 font. But we did */
			 /*  this code won't work if we follow type2 conventions */
			 /*  so turn off type2 until we get 0 callothersubrs */
			 /*  which marks the end of the flex sequence */
			 is_type2=false;
			 if (cur != NULL) {
			    oldcur=cur;
			    cur->next=NULL;
			 } else
			    ErrorMsg(2,"Bad flex subroutine in %s\n", name);
		      }
		      break;
		   case 2:{
			 /* No op */ ;
		      }
		      break;
		   case 0:
		      if (oldcur != NULL) {
			 SplinePointList *spl=oldcur->next;

			 if (spl != NULL && spl->next != NULL &&
			     spl->next->next != NULL &&
			     spl->next->next->next != NULL &&
			     spl->next->next->next->next != NULL &&
			     spl->next->next->next->next->next != NULL &&
			     spl->next->next->next->next->next->next !=
			     NULL) {
			    BasePoint old_nextcp, mid_prevcp, mid, mid_nextcp,
			       end_prevcp, end;
			    old_nextcp=spl->next->first->me;
			    mid_prevcp=spl->next->next->first->me;
			    mid=spl->next->next->next->first->me;
			    mid_nextcp =
			       spl->next->next->next->next->first->me;
			    end_prevcp =
			       spl->next->next->next->next->next->first->me;
			    end =
			       spl->next->next->next->next->next->next->
			       first->me;
			    cur=oldcur;
			    if (cur != NULL && cur->first != NULL
				&& (cur->first != cur->last
				    || cur->first->next==NULL)) {
			       cur->last->nextcp=old_nextcp;
			       cur->last->nonextcp=false;
			       pt=chunkalloc(sizeof(SplinePoint));
			       pt->hintmask=pending_hm;
			       pending_hm=NULL;
			       pt->prevcp=mid_prevcp;
			       pt->me=mid;
			       pt->nextcp=mid_nextcp;
			       /*pt->flex=pops[2]; */
			       CheckMake(cur->last, pt);
			       SplineMake3(cur->last, pt);
			       cur->last=pt;
			       pt=chunkalloc(sizeof(SplinePoint));
			       pt->prevcp=end_prevcp;
			       pt->me=end;
			       pt->nonextcp=true;
			       CheckMake(cur->last, pt);
			       SplineMake3(cur->last, pt);
			       cur->last=pt;
			    } else
			       ErrorMsg(2,"No previous point on path in curveto from flex 0 in %s\n",
					name);
			 } else {
			    /* Um, something's wrong. Let's just draw a line */
			    /* do the simple method, which consists of creating */
			    /*  the appropriate line */
			    pt=chunkalloc(sizeof(SplinePoint));
			    pt->me.x=pops[1];
			    pt->me.y=pops[0];
			    pt->noprevcp=true;
			    pt->nonextcp=true;
			    SplinePointListFree(oldcur->next);
			    oldcur->next=NULL;
			    spl=NULL;
			    cur=oldcur;
			    if (cur != NULL && cur->first != NULL
				&& (cur->first != cur->last
				    || cur->first->next==NULL)) {
			       CheckMake(cur->last, pt);
			       SplineMake3(cur->last, pt);
			       cur->last=pt;
			    } else
			       ErrorMsg(2,"No previous point on path in lineto from flex 0 in %s\n",
					name);
			 }
			 --popsp;
			 cur->next=NULL;
			 SplinePointListsFree(spl);
			 oldcur=NULL;
		      } else
			 ErrorMsg(2,"Bad flex subroutine in %s\n", name);

		      is_type2=context->is_type2;
		      /* If we found a type2 font with a type1 flex sequence */
		      /*  (an illegal idea, but never mind, someone gave us one) */
		      /*  then we had to turn off type2 untill the end of the */
		      /*  flex sequence. Which is here */
		      break;
		   case 14:	/* results in 1 blended value */
		   case 15:	/* results in 2 blended values */
		   case 16:	/* results in 3 blended values */
		   case 17:	/* results in 4 blended values */
		   case 18:{	/* results in 6 blended values */
			 int cnt=stack[sp-1]-13;

			 if (cnt==5)
			    cnt=6;
			 if (context->instance_count==0)
			    ErrorMsg(2,"Attempt to use a multiple master subroutine in a non-mm font in %s.\n",
				     name);
			 else if (tot != cnt * context->instance_count)
			    ErrorMsg(2,"Multiple master subroutine called with the wrong number of arguments in %s.\n",
				     name);
			 else {
			    /* Hints need to keep track of the original blends */
			    if (cnt==1 && !is_type2) {
			       if (sp-2-tot >= 1
				   && (!old_last_was_b1
				       || stack[0] != Blend(unblended[1],
							    context))) {
				  unblended[0][0]=stack[0];
				  for (i=1; i<context->instance_count;
				       ++i)
				     unblended[0][i]=0;
			       } else
				  memcpy(unblended, unblended+1,
					 context->instance_count *
					 sizeof(double));
			       for (j=0; j<context->instance_count; ++j)
				  unblended[1][j]=stack[sp-2-tot+j];
			    } else if (cnt==2 && !is_type2) {
			       unblended[0][0]=stack[sp-2-tot];
			       unblended[1][0]=stack[sp-2-tot+1];
			       for (i=0; i<2; ++i)
				  for (j=1; j<context->instance_count;
				       ++j)
				     unblended[i][j] =
					stack[sp-2-tot+2 +
					      i * (context->instance_count -
						   1)+(j-1)];
			    }
			    popsp=0;
			    for (i=0; i<cnt; ++i) {
			       double sum=stack[sp-2-tot+i];

			       for (j=1; j<context->instance_count; ++j)
				  sum += context->blend_values[j] *
				     stack[sp-2-tot+cnt +
					   i * (context->instance_count-1) +
					   j-1];
			       pops[cnt-1-popsp++]=sum;
			    }
			 }
		      }
		      break;
		 }
		 sp=k+1;
	      }
	      break;
	   case 20:		/* put */
	      if (sp<2)
		 ErrorMsg(2,"Too few items on stack for put in %s\n", name);
	      else if (stack[sp-1]<0 || stack[sp-1] >= 32)
		 ErrorMsg(2,"Reference to transient memory out of bounds in put in %s\n",
			  name);
	      else {
		 transient[(int) stack[sp-1]]=stack[sp-2];
		 sp -= 2;
	      }
	      break;
	   case 21:		/* get */
	      if (sp<1)
		 ErrorMsg(2,"Too few items on stack for get in %s\n", name);
	      else if (stack[sp-1]<0 || stack[sp-1] >= 32)
		 ErrorMsg(2,"Reference to transient memory out of bounds in put in %s\n",
			  name);
	      else
		 stack[sp-1]=transient[(int) stack[sp-1]];
	      break;
	   case 17:		/* pop */
	      /* pops something from the postscript stack and pushes it on ours */
	      /* used to get a return value from an othersubr call */
	      /* Bleah. Adobe wants the pops to return the arguments if we */
	      /*  don't understand the call. What use is the subroutine then? */
	      if (popsp <= 0)
		 ErrorMsg(2,"Pop stack underflow on pop in %s\n", name);
	      else
		 stack[sp++]=pops[--popsp];
	      break;
	   case 18:		/* drop */
	      if (sp>0)
		 --sp;
	      break;
	   case 27:		/* dup */
	      if (sp >= 1) {
		 stack[sp]=stack[sp-1];
		 ++sp;
	      }
	      break;
	   case 28:		/* exch */
	      if (sp >= 2) {
		 double temp=stack[sp-1];

		 stack[sp-1]=stack[sp-2];
		 stack[sp-2]=temp;
	      }
	      break;
	   case 29:		/* index */
	      if (sp >= 1) {
		 int index=stack[--sp];

		 if (index<0 || sp<index+1)
		    ErrorMsg(2,"Index out of range in %s\n", name);
		 else {
		    stack[sp]=stack[sp-index-1];
		    ++sp;
		 }
	      }
	      break;
	   case 30:		/* roll */
	      if (sp >= 2) {
		 int j=stack[sp-1], N=stack[sp-2];

		 if (N>sp || j >= N || j<0 || N<0)
		    ErrorMsg(2,"roll out of range in %s\n", name);
		 else if (j==0 || N==0)
		    /* No op */ ;
		 else {
		    double *temp=malloc(N * sizeof(double));
		    int i;

		    for (i=0; i<N; ++i)
		       temp[i]=stack[sp-N+i];
		    for (i=0; i<N; ++i)
		       stack[sp-N+i]=temp[(i+j)%N];
		    free(temp);
		 }
	      }
	      break;
	   case 33:		/* setcurrentpoint */
	      if (is_type2)
		 ErrorMsg(2,"Type2 fonts do not support the Type1 setcurrentpoint operator\n");
	      if (sp<2)
		 ErrorMsg(2,"Stack underflow on setcurrentpoint in %s\n",
			  name);
	      else {
		 current.x=stack[0];
		 current.y=stack[1];
	      }
	      sp=0;
	      break;
	   case 34:		/* hflex */
	   case 35:		/* flex */
	   case 36:		/* hflex1 */
	   case 37:		/* flex1 */
	      dy=dy3=dy4=dy5=dy6=0;
	      dx=stack[base++];
	      if (v != 34)
		 dy=stack[base++];
	      dx2=stack[base++];
	      dy2=stack[base++];
	      dx3=stack[base++];
	      if (v != 34 && v != 36)
		 dy3=stack[base++];
	      dx4=stack[base++];
	      if (v != 34 && v != 36)
		 dy4=stack[base++];
	      dx5=stack[base++];
	      if (v==34)
		 dy5=-dy2;
	      else
		 dy5=stack[base++];
	      switch (v) {
		   double xt, yt;

		case 35:	/* flex */
		   dx6=stack[base++];
		   dy6=stack[base++];
		   break;
		case 34:	/* hflex */
		   dx6=stack[base++];
		   break;
		case 36:	/* hflex1 */
		   dx6=stack[base++];
		   dy6=-dy-dy2-dy5;
		   break;
		case 37:	/* flex1 */
		   xt=dx+dx2+dx3+dx4+dx5;
		   yt=dy+dy2+dy3+dy4+dy5;
		   if (xt<0)
		      xt=-xt;
		   if (yt<0)
		      yt=-yt;
		   if (xt>yt) {
		      dx6=stack[base++];
		      dy6=-dy-dy2-dy3-dy4-dy5;
		   } else {
		      dy6=stack[base++];
		      dx6=-dx-dx2-dx3-dx4-dx5;
		   }
		   break;
	      }
	      if (cur != NULL && cur->first != NULL
		  && (cur->first != cur->last || cur->first->next==NULL)) {
		 current.x=rint((current.x+dx)*1024)/1024;
		 current.y=rint((current.y+dy)*1024)/1024;
		 cur->last->nextcp.x=current.x;
		 cur->last->nextcp.y=current.y;
		 cur->last->nonextcp=false;
		 current.x=rint((current.x+dx2)*1024)/1024;
		 current.y=rint((current.y+dy2)*1024)/1024;
		 pt=chunkalloc(sizeof(SplinePoint));
		 pt->hintmask=pending_hm;
		 pending_hm=NULL;
		 pt->prevcp.x=current.x;
		 pt->prevcp.y=current.y;
		 current.x=rint((current.x+dx3)*1024)/1024;
		 current.y=rint((current.y+dy3)*1024)/1024;
		 pt->me.x=current.x;
		 pt->me.y=current.y;
		 pt->nonextcp=true;
		 CheckMake(cur->last, pt);
		 SplineMake3(cur->last, pt);
		 cur->last=pt;

		 current.x=rint((current.x+dx4)*1024)/1024;
		 current.y=rint((current.y+dy4)*1024)/1024;
		 cur->last->nextcp.x=current.x;
		 cur->last->nextcp.y=current.y;
		 cur->last->nonextcp=false;
		 current.x=rint((current.x+dx5)*1024)/1024;
		 current.y=rint((current.y+dy5)*1024)/1024;
		 pt=chunkalloc(sizeof(SplinePoint));
		 pt->prevcp.x=current.x;
		 pt->prevcp.y=current.y;
		 current.x=rint((current.x+dx6)*1024)/1024;
		 current.y=rint((current.y+dy6)*1024)/1024;
		 pt->me.x=current.x;
		 pt->me.y=current.y;
		 pt->nonextcp=true;
		 CheckMake(cur->last, pt);
		 SplineMake3(cur->last, pt);
		 cur->last=pt;
	      } else
		 ErrorMsg(2,"No previous point on path in flex operator in %s\n",
			  name);
	      sp=0;
	      break;
	   default:
	      ErrorMsg(2,"Uninterpreted opcode 12,%d in %s\n", v, name);
	      break;
	 }
      } else {
	 last_was_b1=false;
	 switch (v) {
	   case 1:		/* hstem */
	   case 18:		/* hstemhm */
	      base=0;
	      if ((sp&1) && ret->width==(int16_t) 0x8000)
		 ret->width=stack[0];
	      if (sp&1)
		 base=1;
	      if (sp-base<2)
		 ErrorMsg(2,"Stack underflow on hstem in %s\n", name);
	      /* stack[0] is absolute y for start of horizontal hint */
	      /*  (actually relative to the y specified as lsidebearing y in sbw */
	      /* stack[1] is relative y for height of hint zone */
	      coord=0;
	      hp=NULL;
	      if (activeh != NULL)
		 for (hp=activeh; hp->next != NULL; hp=hp->next);
	      while (sp-base >= 2) {
		 sameh=NULL;
		 if (!is_type2)
		    sameh =
		       SameH(ret->hstem, stack[base]+coord, stack[base+1],
			     unblended, context->instance_count);
		 hint=HintNew(stack[base]+coord, stack[base+1]);
		 hint->hintnumber =
		    sameh != NULL?sameh->hintnumber:hint_cnt++;
		 if (!is_type2 && context->instance_count != 0) {
		    hint->u.unblended=chunkalloc(sizeof(double[2][MmMax]));
		    memcpy(hint->u.unblended, unblended,
			   sizeof(double[2][MmMax]));
		 }
		 if (activeh==NULL)
		    activeh=hint;
		 else
		    hp->next=hint;
		 hp=hint;
		 if (!is_type2 && hint->hintnumber<96) {
		    if (pending_hm==NULL)
		       pending_hm=chunkalloc(sizeof(HintMask));
		    (*pending_hm)[hint->hintnumber >> 3] |=
		       0x80 >> (hint->hintnumber&0x7);
		 }
		 base += 2;
		 coord=hint->start+hint->width;
	      }
	      sp=0;
	      break;
	   case 19:		/* hintmask */
	   case 20:		/* cntrmask */
	      /* If there's anything on the stack treat it as a vstem hint */
	   case 3:		/* vstem */
	   case 23:		/* vstemhm */
	      base=0;
	      if (cur==NULL || v==3 || v==23) {
		 if ((sp&1) && is_type2 && ret->width==(int16_t) 0x8000) {
		    ret->width=stack[0];
		 }
		 if (sp&1)
		    base=1;
		 /* I've seen a vstemhm with no arguments. I've no idea what that */
		 /*  means. It came right after a hintmask */
		 /* I'm confused about v/hstemhm because the manual says it needs */
		 /*  to be used if one uses a hintmask, but that's not what the */
		 /*  examples show.  Or I'm not understanding. */
		 if (sp-base<2 && v != 19 && v != 20)
		    ErrorMsg(2,"Stack underflow on vstem in %s\n", name);
		 /* stack[0] is absolute x for start of vertical hint */
		 /*      (actually relative to the x specified as lsidebearing in h/sbw */
		 /* stack[1] is relative x for height of hint zone */
		 coord=ret->lsidebearing;
		 hp=NULL;
		 if (activev != NULL)
		    for (hp=activev; hp->next != NULL; hp=hp->next);
		 while (sp-base >= 2) {
		    sameh=NULL;
		    if (!is_type2)
		       sameh =
			  SameH(ret->vstem, stack[base]+coord,
				stack[base+1], unblended,
				context->instance_count);
		    hint=HintNew(stack[base]+coord, stack[base+1]);
		    hint->hintnumber =
		       sameh != NULL?sameh->hintnumber:hint_cnt++;
		    if (!is_type2 && context->instance_count != 0) {
		       hint->u.unblended=chunkalloc(sizeof(double[2][MmMax]));
		       memcpy(hint->u.unblended, unblended,
			      sizeof(double[2][MmMax]));
		    }
		    if (!is_type2 && hint->hintnumber<96) {
		       if (pending_hm==NULL)
			  pending_hm=chunkalloc(sizeof(HintMask));
		       (*pending_hm)[hint->hintnumber >> 3] |=
			  0x80 >> (hint->hintnumber&0x7);
		    }
		    if (activev==NULL)
		       activev=hint;
		    else
		       hp->next=hint;
		    hp=hint;
		    base += 2;
		    coord=hint->start+hint->width;
		 }
		 sp=0;
	      }
	      if (v==19 || v==20) {	/* hintmask, cntrmask */
		 int bytes=(hint_cnt+7)/8;

		 if (bytes>sizeof(HintMask))
		    bytes=sizeof(HintMask);
		 if (v==19) {
		    ret->hstem=HintsAppend(ret->hstem, activeh);
		    activeh=NULL;
		    ret->vstem=HintsAppend(ret->vstem, activev);
		    activev=NULL;
		    if (pending_hm==NULL)
		       pending_hm=chunkalloc(sizeof(HintMask));
		    memcpy(pending_hm, type1, bytes);
		 } else if (cp<sizeof(counters)/sizeof(counters[0])) {
		    counters[cp]=chunkalloc(sizeof(HintMask));
		    memcpy(counters[cp], type1, bytes);
		    ++cp;
		 }
		 if (bytes != hint_cnt/8) {
		    int mask=0xff >> (hint_cnt&7);

		    if (type1[bytes-1]&mask)
		       ErrorMsg(2,"Hint mask (or counter mask) with too many hints in %s\n",
				name);
		 }
		 type1 += bytes;
		 len -= bytes;
	      }
	      break;
	   case 14:		/* endchar */
	      /* endchar is allowed to terminate processing even within a subroutine */
	      if ((sp&1) && is_type2 && ret->width==(int16_t) 0x8000)
		 ret->width=stack[0];
	      if (context->painttype != 2)
		 closepath(cur, is_type2);
	      pcsp=0;
	      if (sp==4) {
		 /* In Type2 strings endchar has a depreciated function of doing */
		 /*  a seac (which doesn't exist at all). Except enchar takes */
		 /*  4 args and seac takes 5. Bleah */
		 stack[4]=stack[3];
		 stack[3]=stack[2];
		 stack[2]=stack[1];
		 stack[1]=stack[0];
		 stack[0]=0;
		 sp=5;
		 goto seac;
	      } else if (sp==5) {
		 /* same as above except also specified a width */
		 stack[0]=0;
		 goto seac;
	      }
	      /* the docs say that endchar must be the last command in a char */
	      /*  (or the last command in a subroutine which is the last in the */
	      /*  char) So in theory if there's anything left we should complain */
	      /*  In practice though, the EuroFont has a return statement after */
	      /*  the endchar in a subroutine. So we won't try to catch that err */
	      /*  and just stop. */
	      /* Adobe says it's not an error, but I can't understand their */
	      /*  logic */
	      goto done;
	      break;
	   case 13:		/* hsbw (set left sidebearing and width) */
	      if (sp<2)
		 ErrorMsg(2,"Stack underflow on hsbw in %s\n", name);
	      ret->lsidebearing=stack[0];
	      current.x=stack[0];	/* sets the current point too */
	      ret->width=stack[1];
	      sp=0;
	      break;
	   case 9:		/* closepath */
	      sp=0;
	      closepath(cur, is_type2);
	      break;
	   case 21:		/* rmoveto */
	   case 22:		/* hmoveto */
	   case 4:		/* vmoveto */
	      if (is_type2) {
		 if (((v==21 && sp==3) || (v != 21 && sp==2))
		     && ret->width==(int16_t) 0x8000)
		    /* Character's width may be specified on the first moveto */
		    ret->width=stack[0];
		 if (v==21 && sp>2) {
		    stack[0]=stack[sp-2];
		    stack[1]=stack[sp-1];
		    sp=2;
		 } else if (v != 21 && sp>1) {
		    stack[0]=stack[sp-1];
		    sp=1;
		 }
		 if (context->painttype != 2)
		    closepath(cur, true);
	      }
	   case 5:		/* rlineto */
	   case 6:		/* hlineto */
	   case 7:		/* vlineto */
	      polarity=0;
	      base=0;
	      while (base<sp) {
		 dx=dy=0;
		 if (v==5 || v==21) {
		    if (sp<base+2) {
		       ErrorMsg(2,"Stack underflow on rlineto/rmoveto in %s\n",
				name);
		       break;
		    }
		    dx=stack[base++];
		    dy=stack[base++];
		 } else if ((v==6 && !(polarity&1))
			    || (v==7 && (polarity&1)) || v==22) {
		    if (sp <= base) {
		       ErrorMsg(2,"Stack underflow on hlineto/hmoveto in %s\n",
				name);
		       break;
		    }
		    dx=stack[base++];
		 } else {	/*if ((v==7 && !(parity&1)) || (v==6 && (parity&1) || v==4 ) */

		    if (sp <= base) {
		       ErrorMsg(2,"Stack underflow on vlineto/vmoveto in %s\n",
				name);
		       break;
		    }
		    dy=stack[base++];
		 }
		 ++polarity;
		 current.x=rint((current.x+dx)*1024)/1024;
		 current.y=rint((current.y+dy)*1024)/1024;
		 pt=chunkalloc(sizeof(SplinePoint));
		 pt->hintmask=pending_hm;
		 pending_hm=NULL;
		 pt->me.x=current.x;
		 pt->me.y=current.y;
		 pt->noprevcp=true;
		 pt->nonextcp=true;
		 if (v==4 || v==21 || v==22) {
		    if (cur != NULL && cur->first==cur->last
			&& cur->first->prev==NULL && is_type2) {
		       /* Two adjacent movetos should not create single point paths */
		       cur->first->me.x=current.x;
		       cur->first->me.y=current.y;
		       SplinePointFree(pt);
		    } else {
		       SplinePointList *spl =
			  chunkalloc(sizeof(SplinePointList));
		       spl->first=spl->last=pt;
		       if (cur != NULL)
			  cur->next=spl;
		       else
			  ret->layers[ly_fore].splines=spl;
		       cur=spl;
		    }
		    break;
		 } else {
		    if (cur != NULL && cur->first != NULL
			&& (cur->first != cur->last
			    || cur->first->next==NULL)) {
		       CheckMake(cur->last, pt);
		       SplineMake3(cur->last, pt);
		       cur->last=pt;
		    } else
		       ErrorMsg(2,"No previous point on path in lineto in %s\n",
				name);
		    if (!is_type2)
		       break;
		 }
	      }
	      sp=0;
	      break;
	   case 25:		/* rlinecurve */
	      base=0;
	      while (sp>base+6) {
		 current.x=rint((current.x+stack[base++])*1024)/1024;
		 current.y=rint((current.y+stack[base++])*1024)/1024;
		 if (cur != NULL) {
		    pt=chunkalloc(sizeof(SplinePoint));
		    pt->hintmask=pending_hm;
		    pending_hm=NULL;
		    pt->me.x=current.x;
		    pt->me.y=current.y;
		    pt->noprevcp=true;
		    pt->nonextcp=true;
		    CheckMake(cur->last, pt);
		    SplineMake3(cur->last, pt);
		    cur->last=pt;
		 }
	      }
	   case 24:		/* rcurveline */
	   case 8:		/* rrcurveto */
	   case 31:		/* hvcurveto */
	   case 30:		/* vhcurveto */
	   case 27:		/* hhcurveto */
	   case 26:		/* vvcurveto */
	      polarity=0;
	      while (sp>base+2) {
		 dx=dy=dx2=dy2=dx3=dy3=0;
		 if (v==8 || v==25 || v==24) {
		    if (sp<6+base) {
		       ErrorMsg(2,"Stack underflow on rrcurveto in %s\n",
				name);
		       base=sp;
		    } else {
		       dx=stack[base++];
		       dy=stack[base++];
		       dx2=stack[base++];
		       dy2=stack[base++];
		       dx3=stack[base++];
		       dy3=stack[base++];
		    }
		 } else if (v==27) {	/* hhcurveto */
		    if (sp<4+base) {
		       ErrorMsg(2,"Stack underflow on hhcurveto in %s\n",
				name);
		       base=sp;
		    } else {
		       if ((sp-base)&1)
			  dy=stack[base++];
		       dx=stack[base++];
		       dx2=stack[base++];
		       dy2=stack[base++];
		       dx3=stack[base++];
		    }
		 } else if (v==26) {	/* vvcurveto */
		    if (sp<4+base) {
		       ErrorMsg(2,"Stack underflow on hhcurveto in %s\n",
				name);
		       base=sp;
		    } else {
		       if ((sp-base)&1)
			  dx=stack[base++];
		       dy=stack[base++];
		       dx2=stack[base++];
		       dy2=stack[base++];
		       dy3=stack[base++];
		    }
		 } else if ((v==31 && !(polarity&1))
			    || (v==30 && (polarity&1))) {
		    if (sp<4+base) {
		       ErrorMsg(2,"Stack underflow on hvcurveto in %s\n",
				name);
		       base=sp;
		    } else {
		       dx=stack[base++];
		       dx2=stack[base++];
		       dy2=stack[base++];
		       dy3=stack[base++];
		       if (sp==base+1)
			  dx3=stack[base++];
		    }
		 } else {	/*if ((v==30 && !(polarity&1)) || (v==31 && (polarity&1)) ) */

		    if (sp<4+base) {
		       ErrorMsg(2,"Stack underflow on vhcurveto in %s\n",
				name);
		       base=sp;
		    } else {
		       dy=stack[base++];
		       dx2=stack[base++];
		       dy2=stack[base++];
		       dx3=stack[base++];
		       if (sp==base+1)
			  dy3=stack[base++];
		    }
		 }
		 ++polarity;
		 if (cur != NULL && cur->first != NULL
		     && (cur->first != cur->last
			 || cur->first->next==NULL)) {
		    current.x=rint((current.x+dx)*1024)/1024;
		    current.y=rint((current.y+dy)*1024)/1024;
		    cur->last->nextcp.x=current.x;
		    cur->last->nextcp.y=current.y;
		    cur->last->nonextcp=false;
		    current.x=rint((current.x+dx2)*1024)/1024;
		    current.y=rint((current.y+dy2)*1024)/1024;
		    pt=chunkalloc(sizeof(SplinePoint));
		    pt->hintmask=pending_hm;
		    pending_hm=NULL;
		    pt->prevcp.x=current.x;
		    pt->prevcp.y=current.y;
		    current.x=rint((current.x+dx3)*1024)/1024;
		    current.y=rint((current.y+dy3)*1024)/1024;
		    pt->me.x=current.x;
		    pt->me.y=current.y;
		    pt->nonextcp=true;
		    CheckMake(cur->last, pt);
		    SplineMake3(cur->last, pt);
		    cur->last=pt;
		 } else
		    ErrorMsg(2,"No previous point on path in curveto in %s\n",
			     name);
	      }
	      if (v==24) {
		 current.x=rint((current.x+stack[base++])*1024)/1024;
		 current.y=rint((current.y+stack[base++])*1024)/1024;
		 if (cur != NULL) {	/* In legal code, cur can't be null here, but I got something illegal... */
		    pt=chunkalloc(sizeof(SplinePoint));
		    pt->hintmask=pending_hm;
		    pending_hm=NULL;
		    pt->me.x=current.x;
		    pt->me.y=current.y;
		    pt->noprevcp=true;
		    pt->nonextcp=true;
		    CheckMake(cur->last, pt);
		    SplineMake3(cur->last, pt);
		    cur->last=pt;
		 }
	      }
	      sp=0;
	      break;
	   case 29:		/* callgsubr */
	   case 10:		/* callsubr */
	      /* stack[sp-1] contains the number of the subroutine to call */
	      if (sp<1) {
		 ErrorMsg(2,"Stack underflow on callsubr in %s\n", name);
		 break;
	      } else if (pcsp>10) {
		 ErrorMsg(2,"Too many subroutine calls in %s\n", name);
		 break;
	      }
	      s=subrs;
	      if (v==29)
		 s=gsubrs;
	      if (s != NULL)
		 stack[sp-1] += s->bias;
	      /* Type2 subrs have a bias that must be added to the subr-number */
	      /* Type1 subrs do not. We set the bias on them to 0 */
	      if (s==NULL || stack[sp-1] >= s->cnt || stack[sp-1]<0 ||
		  s->values[(int) stack[sp-1]]==NULL)
		 ErrorMsg(2,"Subroutine number out of bounds in %s\n", name);
	      else {
		 pcstack[pcsp].type1=type1;
		 pcstack[pcsp].len=len;
		 pcstack[pcsp].subnum=stack[sp-1];
		 ++pcsp;
		 type1=s->values[(int) stack[sp-1]];
		 len=s->lens[(int) stack[sp-1]];
	      }
	      if (--sp<0)
		 sp=0;
	      break;
	   case 11:		/* return */
	      /* return from a subr outine */
	      if (pcsp<1)
		 ErrorMsg(2,"return when not in subroutine in %s\n", name);
	      else {
		 --pcsp;
		 type1=pcstack[pcsp].type1;
		 len=pcstack[pcsp].len;
	      }
	      break;
	   case 16:{		/* blend -- obsolete type 2 multiple master operator */
		 int cnt, i, j;

		 if (context->instance_count==0)
		    ErrorMsg(2,"Attempt to use a multiple master subroutine in a non-mm font.\n");
		 else if (sp<1
			  || sp<context->instance_count * stack[sp-1]+1)
		    ErrorMsg(2,"Too few items on stack for blend in %s\n",
			     name);
		 else {
		    if (!context->blend_warn) {
		       ErrorMsg(2,"Use of obsolete blend operator.\n");
		       context->blend_warn=true;
		    }
		    cnt=stack[sp-1];
		    sp -= context->instance_count * stack[sp-1]+1;
		    for (i=0; i<cnt; ++i) {
		       for (j=1; j<context->instance_count; ++j)
			  stack[sp+i] +=
			     context->blend_values[j] * stack[sp+cnt +
							      i *
							      (context->
							       instance_count
							      -1)+j-1];
		    }
		    /* there will always be fewer pushes than there were pops */
		    /*  so I don't bother to check the stack */
		    sp += cnt;
		 }
	      }
	      break;
	   default:
	      ErrorMsg(2,"Uninterpreted opcode %d in %s\n", v, name);
	      break;
	 }
      }
   }
 done:
   if (pcsp != 0)
      ErrorMsg(2,"end of subroutine reached with no return in %s\n", name);
   SCCategorizePoints(ret);

   ret->hstem=HintsAppend(ret->hstem, activeh);
   activeh=NULL;
   ret->vstem=HintsAppend(ret->vstem, activev);
   activev=NULL;

   if (cp != 0) {
      int i;

      ret->countermasks=malloc(cp * sizeof(HintMask));
      ret->countermask_cnt=cp;
      for (i=0; i<cp; ++i) {
	 memcpy(&ret->countermasks[i], counters[i], sizeof(HintMask));
	 chunkfree(counters[i], sizeof(HintMask));
      }
   }

   /* Even in type1 fonts all paths should be closed. But if we close them at */
   /*  the obvious moveto, that breaks flex hints. So we have a hack here at */
   /*  the end which closes any open paths. */
   /* If we do have a PaintType 2 font, then presumably the difference between */
   /*  open and closed paths matters */
   if (!is_type2 && !context->painttype)
      for (cur=ret->layers[ly_fore].splines; cur != NULL; cur=cur->next)
	 if (cur->first->prev==NULL) {
	    CheckMake(cur->last, cur->first);
	    SplineMake3(cur->last, cur->first);
	    cur->last=cur->first;
	 }

   /* Oh, I see. PS and TT disagree on which direction to use, so Fontographer */
   /*  chose the TT direction and we must reverse postscript */
   for (cur=ret->layers[ly_fore].splines; cur != NULL; cur=cur->next)
      SplineSetReverse(cur);
   if (ret->hstem==NULL && ret->vstem==NULL)
      ret->manualhints=false;
   if (!is_type2 && context->instance_count != 0) {
      UnblendFree(ret->hstem);
      UnblendFree(ret->vstem);
   }
   ret->hstem=HintCleanup(ret->hstem, true, context->instance_count);
   ret->vstem=HintCleanup(ret->vstem, true, context->instance_count);
   SCGuessHHintInstancesList(ret, ly_fore);
   SCGuessVHintInstancesList(ret, ly_fore);
   ret->hconflicts=StemListAnyConflicts(ret->hstem);
   ret->vconflicts=StemListAnyConflicts(ret->vstem);
   if (context->instance_count==1 && !ret->hconflicts && !ret->vconflicts)
      SCClearHintMasks(ret, ly_fore, false);
   HintsRenumber(ret);
   if (name != NULL && strcmp(name, ".notdef") != 0)
      ret->widthset=true;
   return (ret);
}
