/* $Id: parsepfa.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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ustring.h>
#include <utype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "psfont.h"

struct fontparse {
   FontDict *fd, *mainfd;
   /* always in font data */
   unsigned int infi:1;		/* in definition of FontInfo dict */
   unsigned int inchars:1;
   unsigned int inprivate:1;
   unsigned int insubs:1;
   unsigned int inmetrics:1;
   unsigned int inmetrics2:1;
   unsigned int inbb:1;
   unsigned int inencoding:1;
   unsigned int simpleencoding:1;
   unsigned int multiline:1;
   unsigned int incidsysteminfo:1;
   unsigned int inblendfi:1;
   unsigned int inblendprivate:1;
   unsigned int skipping_mbf:1;
   unsigned int inblend:1;
   unsigned int iscid:1;
   unsigned int iscff:1;
   unsigned int useshexstrings:1;
   unsigned int doneencoding:1;
   unsigned int ignore:1;
   int simple_enc_pos;
   int instring;
   int fdindex;
   char **pending_parse;
   AFILE *sfnts;
   unsigned int alreadycomplained:1;
   char *vbuf, *vmax, *vpt;
   int depth;
};

static void copyenc(char *encoding[256],char *std[256]) {
   int i;

   for (i=0; i<256; ++i)
      encoding[i]=fastrdup(std[i]);
}

char *AdobeStandardEncoding[]={
/* 0000 */ ".notdef",
/* 0001 */ ".notdef",
/* 0002 */ ".notdef",
/* 0003 */ ".notdef",
/* 0004 */ ".notdef",
/* 0005 */ ".notdef",
/* 0006 */ ".notdef",
/* 0007 */ ".notdef",
/* 0008 */ ".notdef",
/* 0009 */ ".notdef",
/* 000a */ ".notdef",
/* 000b */ ".notdef",
/* 000c */ ".notdef",
/* 000d */ ".notdef",
/* 000e */ ".notdef",
/* 000f */ ".notdef",
/* 0010 */ ".notdef",
/* 0011 */ ".notdef",
/* 0012 */ ".notdef",
/* 0013 */ ".notdef",
/* 0014 */ ".notdef",
/* 0015 */ ".notdef",
/* 0016 */ ".notdef",
/* 0017 */ ".notdef",
/* 0018 */ ".notdef",
/* 0019 */ ".notdef",
/* 001a */ ".notdef",
/* 001b */ ".notdef",
/* 001c */ ".notdef",
/* 001d */ ".notdef",
/* 001e */ ".notdef",
/* 001f */ ".notdef",
/* 0020 */ "space",
/* 0021 */ "exclam",
/* 0022 */ "quotedbl",
/* 0023 */ "numbersign",
/* 0024 */ "dollar",
/* 0025 */ "percent",
/* 0026 */ "ampersand",
/* 0027 */ "quoteright",
/* 0028 */ "parenleft",
/* 0029 */ "parenright",
/* 002a */ "asterisk",
/* 002b */ "plus",
/* 002c */ "comma",
/* 002d */ "hyphen",
/* 002e */ "period",
/* 002f */ "slash",
/* 0030 */ "zero",
/* 0031 */ "one",
/* 0032 */ "two",
/* 0033 */ "three",
/* 0034 */ "four",
/* 0035 */ "five",
/* 0036 */ "six",
/* 0037 */ "seven",
/* 0038 */ "eight",
/* 0039 */ "nine",
/* 003a */ "colon",
/* 003b */ "semicolon",
/* 003c */ "less",
/* 003d */ "equal",
/* 003e */ "greater",
/* 003f */ "question",
/* 0040 */ "at",
/* 0041 */ "A",
/* 0042 */ "B",
/* 0043 */ "C",
/* 0044 */ "D",
/* 0045 */ "E",
/* 0046 */ "F",
/* 0047 */ "G",
/* 0048 */ "H",
/* 0049 */ "I",
/* 004a */ "J",
/* 004b */ "K",
/* 004c */ "L",
/* 004d */ "M",
/* 004e */ "N",
/* 004f */ "O",
/* 0050 */ "P",
/* 0051 */ "Q",
/* 0052 */ "R",
/* 0053 */ "S",
/* 0054 */ "T",
/* 0055 */ "U",
/* 0056 */ "V",
/* 0057 */ "W",
/* 0058 */ "X",
/* 0059 */ "Y",
/* 005a */ "Z",
/* 005b */ "bracketleft",
/* 005c */ "backslash",
/* 005d */ "bracketright",
/* 005e */ "asciicircum",
/* 005f */ "underscore",
/* 0060 */ "quoteleft",
/* 0061 */ "a",
/* 0062 */ "b",
/* 0063 */ "c",
/* 0064 */ "d",
/* 0065 */ "e",
/* 0066 */ "f",
/* 0067 */ "g",
/* 0068 */ "h",
/* 0069 */ "i",
/* 006a */ "j",
/* 006b */ "k",
/* 006c */ "l",
/* 006d */ "m",
/* 006e */ "n",
/* 006f */ "o",
/* 0070 */ "p",
/* 0071 */ "q",
/* 0072 */ "r",
/* 0073 */ "s",
/* 0074 */ "t",
/* 0075 */ "u",
/* 0076 */ "v",
/* 0077 */ "w",
/* 0078 */ "x",
/* 0079 */ "y",
/* 007a */ "z",
/* 007b */ "braceleft",
/* 007c */ "bar",
/* 007d */ "braceright",
/* 007e */ "asciitilde",
/* 007f */ ".notdef",
/* 0080 */ ".notdef",
/* 0081 */ ".notdef",
/* 0082 */ ".notdef",
/* 0083 */ ".notdef",
/* 0084 */ ".notdef",
/* 0085 */ ".notdef",
/* 0086 */ ".notdef",
/* 0087 */ ".notdef",
/* 0088 */ ".notdef",
/* 0089 */ ".notdef",
/* 008a */ ".notdef",
/* 008b */ ".notdef",
/* 008c */ ".notdef",
/* 008d */ ".notdef",
/* 008e */ ".notdef",
/* 008f */ ".notdef",
/* 0090 */ ".notdef",
/* 0091 */ ".notdef",
/* 0092 */ ".notdef",
/* 0093 */ ".notdef",
/* 0094 */ ".notdef",
/* 0095 */ ".notdef",
/* 0096 */ ".notdef",
/* 0097 */ ".notdef",
/* 0098 */ ".notdef",
/* 0099 */ ".notdef",
/* 009a */ ".notdef",
/* 009b */ ".notdef",
/* 009c */ ".notdef",
/* 009d */ ".notdef",
/* 009e */ ".notdef",
/* 009f */ ".notdef",
/* 00a0 */ ".notdef",
/* 00a1 */ "exclamdown",
/* 00a2 */ "cent",
/* 00a3 */ "sterling",
/* 00a4 */ "fraction",
/* 00a5 */ "yen",
/* 00a6 */ "florin",
/* 00a7 */ "section",
/* 00a8 */ "currency",
/* 00a9 */ "quotesingle",
/* 00aa */ "quotedblleft",
/* 00ab */ "guillemotleft",
/* 00ac */ "guilsinglleft",
/* 00ad */ "guilsinglright",
/* 00ae */ "fi",
/* 00af */ "fl",
/* 00b0 */ ".notdef",
/* 00b1 */ "endash",
/* 00b2 */ "dagger",
/* 00b3 */ "daggerdbl",
/* 00b4 */ "periodcentered",
/* 00b5 */ ".notdef",
/* 00b6 */ "paragraph",
/* 00b7 */ "bullet",
/* 00b8 */ "quotesinglbase",
/* 00b9 */ "quotedblbase",
/* 00ba */ "quotedblright",
/* 00bb */ "guillemotright",
/* 00bc */ "ellipsis",
/* 00bd */ "perthousand",
/* 00be */ ".notdef",
/* 00bf */ "questiondown",
/* 00c0 */ ".notdef",
/* 00c1 */ "grave",
/* 00c2 */ "acute",
/* 00c3 */ "circumflex",
/* 00c4 */ "tilde",
/* 00c5 */ "macron",
/* 00c6 */ "breve",
/* 00c7 */ "dotaccent",
/* 00c8 */ "dieresis",
/* 00c9 */ ".notdef",
/* 00ca */ "ring",
/* 00cb */ "cedilla",
/* 00cc */ ".notdef",
/* 00cd */ "hungarumlaut",
/* 00ce */ "ogonek",
/* 00cf */ "caron",
/* 00d0 */ "emdash",
/* 00d1 */ ".notdef",
/* 00d2 */ ".notdef",
/* 00d3 */ ".notdef",
/* 00d4 */ ".notdef",
/* 00d5 */ ".notdef",
/* 00d6 */ ".notdef",
/* 00d7 */ ".notdef",
/* 00d8 */ ".notdef",
/* 00d9 */ ".notdef",
/* 00da */ ".notdef",
/* 00db */ ".notdef",
/* 00dc */ ".notdef",
/* 00dd */ ".notdef",
/* 00de */ ".notdef",
/* 00df */ ".notdef",
/* 00e0 */ ".notdef",
/* 00e1 */ "AE",
/* 00e2 */ ".notdef",
/* 00e3 */ "ordfeminine",
/* 00e4 */ ".notdef",
/* 00e5 */ ".notdef",
/* 00e6 */ ".notdef",
/* 00e7 */ ".notdef",
/* 00e8 */ "Lslash",
/* 00e9 */ "Oslash",
/* 00ea */ "OE",
/* 00eb */ "ordmasculine",
/* 00ec */ ".notdef",
/* 00ed */ ".notdef",
/* 00ee */ ".notdef",
/* 00ef */ ".notdef",
/* 00f0 */ ".notdef",
/* 00f1 */ "ae",
/* 00f2 */ ".notdef",
/* 00f3 */ ".notdef",
/* 00f4 */ ".notdef",
/* 00f5 */ "dotlessi",
/* 00f6 */ ".notdef",
/* 00f7 */ ".notdef",
/* 00f8 */ "lslash",
/* 00f9 */ "oslash",
/* 00fa */ "oe",
/* 00fb */ "germandbls",
/* 00fc */ ".notdef",
/* 00fd */ ".notdef",
/* 00fe */ ".notdef",
/* 00ff */ ".notdef"
};
static void setStdEnc(char *encoding[256]) {
   copyenc(encoding, AdobeStandardEncoding);
}

static void setLatin1Enc(char *encoding[256]) {
   static char *latin1enc[]={
/* 0000 */ ".notdef",
/* 0001 */ ".notdef",
/* 0002 */ ".notdef",
/* 0003 */ ".notdef",
/* 0004 */ ".notdef",
/* 0005 */ ".notdef",
/* 0006 */ ".notdef",
/* 0007 */ ".notdef",
/* 0008 */ ".notdef",
/* 0009 */ ".notdef",
/* 000a */ ".notdef",
/* 000b */ ".notdef",
/* 000c */ ".notdef",
/* 000d */ ".notdef",
/* 000e */ ".notdef",
/* 000f */ ".notdef",
/* 0010 */ ".notdef",
/* 0011 */ ".notdef",
/* 0012 */ ".notdef",
/* 0013 */ ".notdef",
/* 0014 */ ".notdef",
/* 0015 */ ".notdef",
/* 0016 */ ".notdef",
/* 0017 */ ".notdef",
/* 0018 */ ".notdef",
/* 0019 */ ".notdef",
/* 001a */ ".notdef",
/* 001b */ ".notdef",
/* 001c */ ".notdef",
/* 001d */ ".notdef",
/* 001e */ ".notdef",
/* 001f */ ".notdef",
/* 0020 */ "space",
/* 0021 */ "exclam",
/* 0022 */ "quotedbl",
/* 0023 */ "numbersign",
/* 0024 */ "dollar",
/* 0025 */ "percent",
/* 0026 */ "ampersand",
/* 0027 */ "quoteright",
/* 0028 */ "parenleft",
/* 0029 */ "parenright",
/* 002a */ "asterisk",
/* 002b */ "plus",
/* 002c */ "comma",
/* 002d */ "hyphen",
/* 002e */ "period",
/* 002f */ "slash",
/* 0030 */ "zero",
/* 0031 */ "one",
/* 0032 */ "two",
/* 0033 */ "three",
/* 0034 */ "four",
/* 0035 */ "five",
/* 0036 */ "six",
/* 0037 */ "seven",
/* 0038 */ "eight",
/* 0039 */ "nine",
/* 003a */ "colon",
/* 003b */ "semicolon",
/* 003c */ "less",
/* 003d */ "equal",
/* 003e */ "greater",
/* 003f */ "question",
/* 0040 */ "at",
/* 0041 */ "A",
/* 0042 */ "B",
/* 0043 */ "C",
/* 0044 */ "D",
/* 0045 */ "E",
/* 0046 */ "F",
/* 0047 */ "G",
/* 0048 */ "H",
/* 0049 */ "I",
/* 004a */ "J",
/* 004b */ "K",
/* 004c */ "L",
/* 004d */ "M",
/* 004e */ "N",
/* 004f */ "O",
/* 0050 */ "P",
/* 0051 */ "Q",
/* 0052 */ "R",
/* 0053 */ "S",
/* 0054 */ "T",
/* 0055 */ "U",
/* 0056 */ "V",
/* 0057 */ "W",
/* 0058 */ "X",
/* 0059 */ "Y",
/* 005a */ "Z",
/* 005b */ "bracketleft",
/* 005c */ "backslash",
/* 005d */ "bracketright",
/* 005e */ "asciicircum",
/* 005f */ "underscore",
/* 0060 */ "grave",
/* 0061 */ "a",
/* 0062 */ "b",
/* 0063 */ "c",
/* 0064 */ "d",
/* 0065 */ "e",
/* 0066 */ "f",
/* 0067 */ "g",
/* 0068 */ "h",
/* 0069 */ "i",
/* 006a */ "j",
/* 006b */ "k",
/* 006c */ "l",
/* 006d */ "m",
/* 006e */ "n",
/* 006f */ "o",
/* 0070 */ "p",
/* 0071 */ "q",
/* 0072 */ "r",
/* 0073 */ "s",
/* 0074 */ "t",
/* 0075 */ "u",
/* 0076 */ "v",
/* 0077 */ "w",
/* 0078 */ "x",
/* 0079 */ "y",
/* 007a */ "z",
/* 007b */ "braceleft",
/* 007c */ "bar",
/* 007d */ "braceright",
/* 007e */ "asciitilde",
/* 007f */ ".notdef",
/* 0080 */ ".notdef",
/* 0081 */ ".notdef",
/* 0082 */ ".notdef",
/* 0083 */ ".notdef",
/* 0084 */ ".notdef",
/* 0085 */ ".notdef",
/* 0086 */ ".notdef",
/* 0087 */ ".notdef",
/* 0088 */ ".notdef",
/* 0089 */ ".notdef",
/* 008a */ ".notdef",
/* 008b */ ".notdef",
/* 008c */ ".notdef",
/* 008d */ ".notdef",
/* 008e */ ".notdef",
/* 008f */ ".notdef",
				/* 0090 */ "dotlessi",
				/* Um, Adobe's Latin1 has some extra chars */
/* 0091 */ "grave",
				/* 0092 */ "accute",
				/* This is a duplicate... */
/* 0093 */ "circumflex",
/* 0094 */ "tilde",
/* 0095 */ "macron",
/* 0096 */ "breve",
/* 0097 */ "dotaccent",
/* 0098 */ "dieresis",
/* 0099 */ ".notdef",
/* 009a */ "ring",
/* 009b */ "cedilla",
/* 009c */ ".notdef",
/* 009d */ "hungarumlaut",
/* 009e */ "ogonek",
/* 009f */ "caron",
/* 00a0 */ "space",
/* 00a1 */ "exclamdown",
/* 00a2 */ "cent",
/* 00a3 */ "sterling",
/* 00a4 */ "currency",
/* 00a5 */ "yen",
/* 00a6 */ "brokenbar",
/* 00a7 */ "section",
/* 00a8 */ "dieresis",
/* 00a9 */ "copyright",
/* 00aa */ "ordfeminine",
/* 00ab */ "guillemotleft",
/* 00ac */ "logicalnot",
/* 00ad */ "hyphen",
/* 00ae */ "registered",
/* 00af */ "macron",
/* 00b0 */ "degree",
/* 00b1 */ "plusminus",
/* 00b2 */ "twosuperior",
/* 00b3 */ "threesuperior",
/* 00b4 */ "acute",
/* 00b5 */ "mu",
/* 00b6 */ "paragraph",
/* 00b7 */ "periodcentered",
/* 00b8 */ "cedilla",
/* 00b9 */ "onesuperior",
/* 00ba */ "ordmasculine",
/* 00bb */ "guillemotright",
/* 00bc */ "onequarter",
/* 00bd */ "onehalf",
/* 00be */ "threequarters",
/* 00bf */ "questiondown",
/* 00c0 */ "Agrave",
/* 00c1 */ "Aacute",
/* 00c2 */ "Acircumflex",
/* 00c3 */ "Atilde",
/* 00c4 */ "Adieresis",
/* 00c5 */ "Aring",
/* 00c6 */ "AE",
/* 00c7 */ "Ccedilla",
/* 00c8 */ "Egrave",
/* 00c9 */ "Eacute",
/* 00ca */ "Ecircumflex",
/* 00cb */ "Edieresis",
/* 00cc */ "Igrave",
/* 00cd */ "Iacute",
/* 00ce */ "Icircumflex",
/* 00cf */ "Idieresis",
/* 00d0 */ "Eth",
/* 00d1 */ "Ntilde",
/* 00d2 */ "Ograve",
/* 00d3 */ "Oacute",
/* 00d4 */ "Ocircumflex",
/* 00d5 */ "Otilde",
/* 00d6 */ "Odieresis",
/* 00d7 */ "multiply",
/* 00d8 */ "Oslash",
/* 00d9 */ "Ugrave",
/* 00da */ "Uacute",
/* 00db */ "Ucircumflex",
/* 00dc */ "Udieresis",
/* 00dd */ "Yacute",
/* 00de */ "Thorn",
/* 00df */ "germandbls",
/* 00e0 */ "agrave",
/* 00e1 */ "aacute",
/* 00e2 */ "acircumflex",
/* 00e3 */ "atilde",
/* 00e4 */ "adieresis",
/* 00e5 */ "aring",
/* 00e6 */ "ae",
/* 00e7 */ "ccedilla",
/* 00e8 */ "egrave",
/* 00e9 */ "eacute",
/* 00ea */ "ecircumflex",
/* 00eb */ "edieresis",
/* 00ec */ "igrave",
/* 00ed */ "iacute",
/* 00ee */ "icircumflex",
/* 00ef */ "idieresis",
/* 00f0 */ "eth",
/* 00f1 */ "ntilde",
/* 00f2 */ "ograve",
/* 00f3 */ "oacute",
/* 00f4 */ "ocircumflex",
/* 00f5 */ "otilde",
/* 00f6 */ "odieresis",
/* 00f7 */ "divide",
/* 00f8 */ "oslash",
/* 00f9 */ "ugrave",
/* 00fa */ "uacute",
/* 00fb */ "ucircumflex",
/* 00fc */ "udieresis",
/* 00fd */ "yacute",
/* 00fe */ "thorn",
/* 00ff */ "ydieresis"
   };
   copyenc(encoding, latin1enc);
}

char *AdobeExpertEncoding[]={
/* 0000 */ ".notdef",
/* 0001 */ ".notdef",
/* 0002 */ ".notdef",
/* 0003 */ ".notdef",
/* 0004 */ ".notdef",
/* 0005 */ ".notdef",
/* 0006 */ ".notdef",
/* 0007 */ ".notdef",
/* 0008 */ ".notdef",
/* 0009 */ ".notdef",
/* 000a */ ".notdef",
/* 000b */ ".notdef",
/* 000c */ ".notdef",
/* 000d */ ".notdef",
/* 000e */ ".notdef",
/* 000f */ ".notdef",
/* 0010 */ ".notdef",
/* 0011 */ ".notdef",
/* 0012 */ ".notdef",
/* 0013 */ ".notdef",
/* 0014 */ ".notdef",
/* 0015 */ ".notdef",
/* 0016 */ ".notdef",
/* 0017 */ ".notdef",
/* 0018 */ ".notdef",
/* 0019 */ ".notdef",
/* 001a */ ".notdef",
/* 001b */ ".notdef",
/* 001c */ ".notdef",
/* 001d */ ".notdef",
/* 001e */ ".notdef",
/* 001f */ ".notdef",
/* 0020 */ "space",
/* 0021 */ "exclamsmall",
/* 0022 */ "Hungarumlautsmal",
/* 0023 */ ".notdef",
/* 0024 */ "dollaroldstyle",
/* 0025 */ "dollarsuperior",
/* 0026 */ "ampersandsmall",
/* 0027 */ "Acutesmall",
/* 0028 */ "parenleftsuperior",
/* 0029 */ "parenrightsuperior",
/* 002a */ "twodotenleader",
/* 002b */ "onedotenleader",
/* 002c */ "comma",
/* 002d */ "hyphen",
/* 002e */ "period",
/* 002f */ "fraction",
/* 0030 */ "zerooldstyle",
/* 0031 */ "oneoldstyle",
/* 0032 */ "twooldstyle",
/* 0033 */ "threeoldstyle",
/* 0034 */ "fouroldstyle",
/* 0035 */ "fiveoldstyle",
/* 0036 */ "sixoldstyle",
/* 0037 */ "sevenoldstyle",
/* 0038 */ "eightoldstyle",
/* 0039 */ "nineoldstyle",
/* 003a */ "colon",
/* 003b */ "semicolon",
/* 003c */ "commasuperior",
/* 003d */ "threequartersemdash",
/* 003e */ "periodsuperior",
/* 003f */ "questionsmall",
/* 0040 */ ".notdef",
/* 0041 */ "asuperior",
/* 0042 */ "bsuperior",
/* 0043 */ "centsuperior",
/* 0044 */ "dsuperior",
/* 0045 */ "esuperior",
/* 0046 */ ".notdef",
/* 0047 */ ".notdef",
/* 0048 */ ".notdef",
/* 0049 */ "isuperior",
/* 004a */ ".notdef",
/* 004b */ ".notdef",
/* 004c */ "lsuperior",
/* 004d */ "msuperior",
/* 004e */ "nsuperior",
/* 004f */ "osuperior",
/* 0050 */ ".notdef",
/* 0051 */ ".notdef",
/* 0052 */ "rsuperior",
/* 0053 */ "ssuperior",
/* 0054 */ "tsuperior",
/* 0055 */ ".notdef",
/* 0056 */ "ff",
/* 0057 */ "fi",
/* 0058 */ "fl",
/* 0059 */ "ffi",
/* 005a */ "ffl",
/* 005b */ "parenleftinferior",
/* 005c */ ".notdef",
/* 005d */ "parenrightinferior",
/* 005e */ "Circumflexsmall",
/* 005f */ "hyphensuperior",
/* 0060 */ "Gravesmall",
/* 0061 */ "Asmall",
/* 0062 */ "Bsmall",
/* 0063 */ "Csmall",
/* 0064 */ "Dsmall",
/* 0065 */ "Esmall",
/* 0066 */ "Fsmall",
/* 0067 */ "Gsmall",
/* 0068 */ "Hsmall",
/* 0069 */ "Ismall",
/* 006a */ "Jsmall",
/* 006b */ "Ksmall",
/* 006c */ "Lsmall",
/* 006d */ "Msmall",
/* 006e */ "Nsmall",
/* 006f */ "Osmall",
/* 0070 */ "Psmall",
/* 0071 */ "Qsmall",
/* 0072 */ "Rsmall",
/* 0073 */ "Ssmall",
/* 0074 */ "Tsmall",
/* 0075 */ "Usmall",
/* 0076 */ "Vsmall",
/* 0077 */ "Wsmall",
/* 0078 */ "Xsmall",
/* 0079 */ "Ysmall",
/* 007a */ "Zsmall",
/* 007b */ "colonmonetary",
/* 007c */ "onefitted",
/* 007d */ "rupiah",
/* 007e */ "Tildesmall",
/* 007f */ ".notdef",
/* 0080 */ ".notdef",
/* 0081 */ ".notdef",
/* 0082 */ ".notdef",
/* 0083 */ ".notdef",
/* 0084 */ ".notdef",
/* 0085 */ ".notdef",
/* 0086 */ ".notdef",
/* 0087 */ ".notdef",
/* 0088 */ ".notdef",
/* 0089 */ ".notdef",
/* 008a */ ".notdef",
/* 008b */ ".notdef",
/* 008c */ ".notdef",
/* 008d */ ".notdef",
/* 008e */ ".notdef",
/* 008f */ ".notdef",
/* 0090 */ ".notdef",
/* 0091 */ ".notdef",
/* 0092 */ ".notdef",
/* 0093 */ ".notdef",
/* 0094 */ ".notdef",
/* 0095 */ ".notdef",
/* 0096 */ ".notdef",
/* 0097 */ ".notdef",
/* 0098 */ ".notdef",
/* 0099 */ ".notdef",
/* 009a */ ".notdef",
/* 009b */ ".notdef",
/* 009c */ ".notdef",
/* 009d */ ".notdef",
/* 009e */ ".notdef",
/* 009f */ ".notdef",
/* 00a0 */ ".notdef",
/* 00a1 */ "exclamdownsmall",
/* 00a2 */ "centoldstyle",
/* 00a3 */ "Lslashsmall",
/* 00a4 */ ".notdef",
/* 00a5 */ ".notdef",
/* 00a6 */ "Scaronsmall",
/* 00a7 */ "Zcaronsmall",
/* 00a8 */ "Dieresissmall",
/* 00a9 */ "Brevesmall",
/* 00aa */ "Caronsmall",
/* 00ab */ ".notdef",
/* 00ac */ "Dotaccentsmall",
/* 00ad */ ".notdef",
/* 00ae */ ".notdef",
/* 00af */ "Macronsmall",
/* 00b0 */ ".notdef",
/* 00b1 */ ".notdef",
/* 00b2 */ "figuredash",
/* 00b3 */ "hypheninferior",
/* 00b4 */ ".notdef",
/* 00b5 */ ".notdef",
/* 00b6 */ "Ogoneksmall",
/* 00b7 */ "Ringsmall",
/* 00b8 */ "Cedillasmall",
/* 00b9 */ ".notdef",
/* 00ba */ ".notdef",
/* 00bb */ ".notdef",
/* 00bc */ "onequarter",
/* 00bd */ "onehalf",
/* 00be */ "threequarters",
/* 00bf */ "questiondownsmall",
/* 00c0 */ "oneeighth",
/* 00c1 */ "threeeighths",
/* 00c2 */ "fiveeighths",
/* 00c3 */ "seveneighths",
/* 00c4 */ "onethird",
/* 00c5 */ "twothirds",
/* 00c6 */ ".notdef",
/* 00c7 */ ".notdef",
/* 00c8 */ "zerosuperior",
/* 00c9 */ "onesuperior",
/* 00ca */ "twosuperior",
/* 00cb */ "threesuperior",
/* 00cc */ "foursuperior",
/* 00cd */ "fivesuperior",
/* 00ce */ "sixsuperior",
/* 00cf */ "sevensuperior",
/* 00d0 */ "eightsuperior",
/* 00d1 */ "ninesuperior",
/* 00d2 */ "zeroinferior",
/* 00d3 */ "oneinferior",
/* 00d4 */ "twoinferior",
/* 00d5 */ "threeinferior",
/* 00d6 */ "fourinferior",
/* 00d7 */ "fiveinferior",
/* 00d8 */ "sixinferior",
/* 00d9 */ "seveninferior",
/* 00da */ "eightinferior",
/* 00db */ "nineinferior",
/* 00dc */ "centinferior",
/* 00dd */ "dollarinferior",
/* 00de */ "periodinferior",
/* 00df */ "commainferior",
/* 00e0 */ "Agravesmall",
/* 00e1 */ "Aacutesmall",
/* 00e2 */ "Acircumflexsmall",
/* 00e3 */ "Atildesmall",
/* 00e4 */ "Adieresissmall",
/* 00e5 */ "Aringsmall",
/* 00e6 */ "AEsmall",
/* 00e7 */ "Ccedillasmall",
/* 00e8 */ "Egravesmall",
/* 00e9 */ "Eacutesmall",
/* 00ea */ "Ecircumflexsmall",
/* 00eb */ "Edieresissmall",
/* 00ec */ "Igravesmall",
/* 00ed */ "Iacutesmall",
/* 00ee */ "Icircumflexsmall",
/* 00ef */ "Idieresissmall",
/* 00f0 */ "Ethsmall",
/* 00f1 */ "Ntildesmall",
/* 00f2 */ "Ogravesmall",
/* 00f3 */ "Oacutesmall",
/* 00f4 */ "Ocircumflexsmall",
/* 00f5 */ "Otildesmall",
/* 00f6 */ "Odieresissmall",
/* 00f7 */ "OEsmall",
/* 00f8 */ "Oslashsmall",
/* 00f9 */ "Ugravesmall",
/* 00fa */ "Uacutesmall",
/* 00fb */ "Ucircumflexsmall",
/* 00fc */ "Udieresissmall",
/* 00fd */ "Yacutesmall",
/* 00fe */ "Thornsmall",
/* 00ff */ "Ydieresissmall"
};

static struct fontdict *MakeEmptyFont(void) {
   struct fontdict *ret;

   ret=calloc(1, sizeof(struct fontdict));
   ret->fontinfo=calloc(1, sizeof(struct fontinfo));
   ret->chars=calloc(1, sizeof(struct pschars));
   ret->private=calloc(1, sizeof(struct private));
   ret->private->subrs=calloc(1, sizeof(struct pschars));
   ret->private->private=calloc(1, sizeof(struct psdict));
   ret->private->leniv=4;
   ret->encoding_name=&custom;
   ret->fontinfo->fstype=-1;
   return (ret);
}

static struct fontdict *PSMakeEmptyFont(void) {
   struct fontdict *ret=MakeEmptyFont();

   ret->charprocs=calloc(1, sizeof(struct charprocs));
   return (ret);
}

static char *myfgets(char *str,int len,AFILE *file) {
   char *pt, *end;
   int ch=0;

   for (pt=str, end=str+len-1;
	pt<end && (ch=agetc(file)) != EOF && ch != '\r' && ch != '\n';
	*pt++=ch);
   if (ch=='\n')
      *pt++='\n';
   else if (ch=='\r') {
      *pt++='\r';
      if ((ch=agetc(file)) != '\n')
	 aungetc(ch, file);
      else
	 *pt++='\n';
   }
   if (pt==str)
      return (NULL);
   *pt='\0';
   return (str);
}

static char *myfgetsNoNulls(char *str,int len,AFILE *file) {
   char *pt, *end;
   int ch=0;

   for (pt=str, end=str+len-1;
	pt<end && (ch=agetc(file)) != EOF && ch != '\r' && ch != '\n';) {
      if (ch != '\0')
	 *pt++=ch;
   }
   if (ch=='\n')
      *pt++='\n';
   else if (ch=='\r') {
      *pt++='\r';
      if ((ch=agetc(file)) != '\n')
	 aungetc(ch, file);
      else
	 *pt++='\n';
   }
   if (pt==str)
      return (NULL);
   *pt='\0';
   return (str);
}

static char *getstring(char *start,AFILE *in) {
   char *end, *ret;
   int parencnt=0, len=0;
   char buffer[512];

   while (1) {
      while (*start != '\0' && *start != '(')
	 ++start;
      if (*start=='\0') {
	 if (myfgetsNoNulls(buffer, sizeof(buffer), in)==NULL)
	    return (fastrdup(""));
	 start=buffer;
      } else
	 break;
   }
   ++start;
   ret=NULL;
   len=1;
   while (1) {
      for (end=start; *end != '\0' && (*end != ')' || parencnt>0); ++end) {
	 if (*end=='\\' && (end[1]=='(' || end[1]==')'))
	    ++end;
	 else if (*end=='(')
	    ++parencnt;
	 else if (*end==')')
	    --parencnt;
      }
      if (end>start) {
	 if (ret==NULL)
	    ret=malloc(end-start+1);
	 else
	    ret=realloc(ret, len+end-start);
	 strncpy(ret+len-1, start, end-start);
	 len += end-start;
	 ret[len-1]='\0';
      }
      if (*end != '\0')
	 break;
      if (myfgetsNoNulls(buffer, sizeof(buffer), in)==NULL)
	 return (ret);
      start=buffer;
   }
   return (ret);
}

static char *gettoken(char *start) {
   char *end, *ret;

   while (*start != '\0' && *start != '/' && *start != '(')
      ++start;
   if (*start=='/' || *start=='(')
      ++start;
   for (end=start;
	*end != '\0' && !isspace(*end) && *end != '[' && *end != '/'
	&& *end != '{' && *end != '(' && *end != ')'; ++end);
   ret=malloc(end-start+1);
   if (end>start)
      strncpy(ret, start, end-start);
   ret[end-start]='\0';
   return (ret);
}

static int getbool(char *start) {
   while (isspace(*start))
      ++start;
   if (*start=='T' || *start=='t')
      return (1);

   return (0);
}

static void fillintarray(int *array,char *start,int maxentries) {
   int i;
   char *end;

   while (*start != '\0' && *start != '[' && *start != '{')
      ++start;
   if (*start=='[' || *start=='{')
      ++start;
   for (i=0; i<maxentries && *start != ']' && *start != '}'; ++i) {
      array[i]=(int) strtod(start, &end);
      if (start==end)
	 return;
      start=end;
      while (isspace(*start))
	 ++start;
   }
}

static void fillrealarray(double *array,char *start,int maxentries) {
   int i;
   char *end;

   while (*start != '\0' && *start != '[' && *start != '{')
      ++start;
   if (*start=='[' || *start=='{')
      ++start;
   for (i=0; i<maxentries && *start != ']' && *start != '}'; ++i) {
      while (isspace(*start))
	 ++start;
      if (isdigit(*start) || *start=='-' || *start=='.')
	 array[i]=strtod(start, &end);
      else if (strncmp(start, "div", 3)==0 && i >= 2) {
	 /* Some of Luc Devroye's fonts have a "div" in the FontMatrix */
	 array[i-2] /= array[i-1];
	 i -= 2;
	 end=start+3;
      } else
	 return;
      if (start==end)
	 return;
      start=end;
      while (isspace(*start))
	 ++start;
   }
}

static void InitDict(struct psdict *dict,char *line) {
   while (*line != '/' && *line != '\0')
      ++line;
   while (!isspace(*line) && *line != '\0')
      ++line;
   dict->cnt += strtol(line, NULL, 10);
   if (dict->next>0) {
      int i;			/* Shouldn't happen, but did in a bad file */

      dict->keys=realloc(dict->keys, dict->cnt * sizeof(char *));
      dict->values=realloc(dict->values, dict->cnt * sizeof(char *));
      for (i=dict->next; i<dict->cnt; ++i) {
	 dict->keys[i]=NULL;
	 dict->values[i]=NULL;
      }
   } else {
      dict->keys=calloc(dict->cnt, sizeof(char *));
      dict->values=calloc(dict->cnt, sizeof(char *));
   }
}

static void InitChars(struct pschars *chars,char *line) {
   while (*line != '/' && *line != '\0')
      ++line;
   while (!isspace(*line) && *line != '\0')
      ++line;
   chars->cnt=strtol(line, NULL, 10);
   if (chars->cnt>0) {
      chars->keys=calloc(chars->cnt, sizeof(char *));
      chars->values=calloc(chars->cnt, sizeof(char *));
      chars->lens=calloc(chars->cnt, sizeof(int));
   }
}

static void InitCharProcs(struct charprocs *cp,char *line) {
   while (*line != '/' && *line != '\0')
      ++line;
   while (!isspace(*line) && *line != '\0')
      ++line;
   cp->cnt=strtol(line, NULL, 10);
   if (cp->cnt>0) {
      cp->keys=calloc(cp->cnt, sizeof(char *));
      cp->values=calloc(cp->cnt, sizeof(SplineChar *));
   }
}

static int mycmp(char *str,char *within,char *end) {
   while (within<end) {
      if (*str != *within)
	 return (*str-*within);
      ++str;
      ++within;
   }
   return (*str=='\0'?0:1);
}

static void ContinueValue(struct fontparse *fp,struct psdict *dict,
			  char *line) {
   int incomment=false;

   while (*line) {
      if (!fp->instring && fp->depth==0 &&
	  (strncmp(line, "def", 3)==0 ||
	   strncmp(line, "|-", 2)==0 || strncmp(line, "ND", 2)==0)) {
	 while (1) {
	    while (fp->vpt>fp->vbuf+1 && isspace(fp->vpt[-1]))
	       --fp->vpt;
	    if (fp->vpt>fp->vbuf+8
		&& strncmp(fp->vpt-8, "noaccess", 8)==0)
	       fp->vpt -= 8;
	    else if (fp->vpt>fp->vbuf+8
		     && strncmp(fp->vpt-8, "readonly", 8)==0)
	       fp->vpt -= 8;
	    else if (fp->vpt>fp->vbuf+4
		     && strncmp(fp->vpt-4, "bind", 4)==0)
	       fp->vpt -= 4;
	    else
	       break;
	 }
	 /* In some URW fonts (Nimbus Sans L, n019003l) we get a complex */
	 /*  expression rather than just an array. This is ok. The expression */
	 /*  converts itself into an array. We could just truncate to the */
	 /*  default array, but I don't see any reason to do so */
	 if (fp->pending_parse != NULL) {
	    *fp->pending_parse=copyn(fp->vbuf, fp->vpt-fp->vbuf);
	    fp->pending_parse=NULL;
	 } else {
	    dict->values[dict->next]=copyn(fp->vbuf, fp->vpt-fp->vbuf);
	    ++dict->next;
	 }
	 fp->vpt=fp->vbuf;
	 fp->multiline=false;
	 return;
      }
      if (fp->vpt >= fp->vmax) {
	 int len=fp->vmax-fp->vbuf+1000, off=fp->vpt-fp->vbuf;

	 fp->vbuf=realloc(fp->vbuf, len);
	 fp->vpt=fp->vbuf+off;
	 fp->vmax=fp->vbuf+len;
      }
      if (fp->instring) {
	 if (*line==')')
	    --fp->instring;
      } else if (incomment) {
	 /* Do Nothing */ ;
      } else if (*line=='(')
	 ++fp->instring;
      else if (*line=='%')
	 incomment=true;
      else if (*line=='[' || *line=='{')
	 ++fp->depth;
      else if (*line=='}' || *line==']')
	 --fp->depth;
      *fp->vpt++=*line++;
   }
}

static void AddValue(struct fontparse *fp,struct psdict *dict,char *line,
		     char *endtok) {
   char *pt;

   if (dict != NULL) {
      if (dict->next >= dict->cnt) {
	 dict->cnt += 10;
	 dict->keys=realloc(dict->keys, dict->cnt * sizeof(char *));
	 dict->values=realloc(dict->values, dict->cnt * sizeof(char *));
      }
      dict->keys[dict->next]=copyn(line+1, endtok-(line+1));
   }
   pt=line+strlen(line)-1;
   while (isspace(*endtok))
      ++endtok;
   while (pt>endtok && isspace(*pt))
      --pt;
   ++pt;
   if (strncmp(pt-3, "def", 3)==0)
      pt -= 3;
   else if (strncmp(pt-2, "|-", 2)==0 || strncmp(pt-2, "ND", 2)==0)
      pt -= 2;
   else {
      fp->multiline=true;
      ContinueValue(fp, dict, endtok);
      return;
   }
   while (1) {
      while (pt-1>endtok && isspace(pt[-1]))
	 --pt;
      if (pt-8>endtok && strncmp(pt-8, "noaccess", 8)==0)
	 pt -= 8;
      else if (pt-8>endtok && strncmp(pt-8, "readonly", 8)==0)
	 pt -= 8;
      else if (pt-4>endtok && strncmp(pt-4, "bind", 4)==0)
	 pt -= 4;
      else
	 break;
   }
   if (dict != NULL) {
      dict->values[dict->next]=copyn(endtok, pt-endtok);
      ++dict->next;
   } else {
      *fp->pending_parse=copyn(endtok, pt-endtok);
      fp->pending_parse=NULL;
   }
}

static int hex(int ch1,int ch2) {
/* Convert two HEX characters to one binary value. Return -1 if error */
/* NOTE: FIXME: parsepdf has an identical routine that can be merged. */
   if (ch1 >= '0' && ch1 <= '9')
      ch1 -= '0';
   else if (ch1 >= 'A' && ch1 <= 'F')
      ch1 -= ('A'-10);
   else if (ch1 >= 'a' && ch1 <= 'f')
      ch1 -= ('a'-10);
   else
      return (-1);

   if (ch2 >= '0' && ch2 <= '9')
      ch2 -= '0';
   else if (ch2 >= 'A' && ch2 <= 'F')
      ch2 -= ('A'-10);
   else if (ch2 >= 'a' && ch2 <= 'f')
      ch2 -= ('a'-10);
   else
      return (-1);

   return ((ch1 << 4)|ch2);
}

unsigned short r;

#define c1	52845
#define c2	22719

static void initcode(void) {
   r=55665;
}

static int decode(unsigned char cypher) {
   unsigned char plain=(cypher ^ (r >> 8));

   r=(cypher+r)*c1+c2;
   return (plain);
}

static void dumpzeros(AFILE *out,unsigned char *zeros,int zcnt) {
   while (--zcnt >= 0)
      aputc(*zeros++, out);
}

static void decodestr(unsigned char *str,int len) {
   unsigned short r=4330;
   unsigned char plain, cypher;

   while (len-->0) {
      cypher=*str;
      plain=(cypher ^ (r >> 8));
      r=(cypher+r)*c1+c2;
      *str++=plain;
   }
}

static void findstring(struct fontparse *fp,struct pschars *subrs,int index,
		       char *nametok, char *str) {
   char buffer[1024], *bpt, *bs, *end=buffer+sizeof(buffer)-1;
   int val;

   while (isspace(*str))
      ++str;
   if (*str=='(') {
      ++str;
      bpt=buffer;
      while (*str != ')' && *str != '\0') {
	 if (*str != '\\')
	    val=*str++;
	 else {
	    if (isdigit(*++str)) {
	       val=*str++-'0';
	       if (isdigit(*str)) {
		  val=(val << 3)|(*str++-'0');
		  if (isdigit(*str))
		     val=(val << 3)|(*str++-'0');
	       }
	    } else
	       val=*str++;
	 }
	 if (bpt<end)
	    *bpt++=val;
      }
      decodestr((unsigned char *) buffer, bpt-buffer);
      bs=buffer+fp->fd->private->leniv;
      if (bpt<bs)
	 bs=bpt;		/* garbage */
      subrs->lens[index]=bpt-bs;
      subrs->keys[index]=fastrdup(nametok);
      subrs->values[index]=malloc(bpt-bs);
      memcpy(subrs->values[index], bs, bpt-bs);
      if (index >= subrs->next)
	 subrs->next=index+1;
   }
}

/* Type42 charstrings are actually numbers */
static void findnumbers(struct fontparse *fp,struct pschars *chars,
			char *str) {
   int val;
   char *end;

   while (1) {
      int index=chars->next;
      char *namestrt;

      while (isspace(*str))
	 ++str;
      if (*str != '/')
	 break;
      namestrt=++str;
      while (isalnum(*str) || *str=='.')
	 ++str;
      *str='\0';
      index=chars->next;

      ++str;
      val=strtol(str, &end, 10);
      chars->lens[index]=0;
      chars->keys[index]=fastrdup(namestrt);
      chars->values[index]=(void *) (intptr_t) val;
      chars->next=index+1;
      str=end;
      while (isspace(*str))
	 ++str;
      if (str[0]=='d' && str[1]=='e' && str[2]=='f')
	 str += 3;
   }
}

static char *rmbinary(char *line) {
   char *pt;

   for (pt=line; *pt; ++pt) {
      if ((*pt<' ' || *pt >= 0x7f) && *pt != '\n') {
	 if (strlen(pt)>5) {
	    pt[0]='.';
	    pt[1]='.';
	    pt[2]='.';
	    pt[3]='\n';
	    pt[4]='\0';
	 } else {
	    pt[0]='\n';
	    pt[1]='\0';
	 }
	 break;
      }
   }
   return (line);
}

/* Does the buffer ending at "pt" end with "str"?  Look back as far as	*/
/* "n" chars. "pt" should point to final character in buffer, and not	*/
/* not a terminating null. Return 1 if matched, else 0 if error		*/
/* go see "Re: [Fontanvil-devel] stuck in infinite loop", 2012August22	*/
static int matchFromBack(const char *pt,const char *str,int n) {
   int i, num_to_check=strlen(str);
   const char *strpt=str+num_to_check-1;

   if (n<num_to_check)
      num_to_check=n;
   for (i=0; i<num_to_check; i++) {
      if (*pt-- != *strpt--)
	 return (0);
   }

   return (1);			/* they matched */
}

/* putBack():								*/
/* Puts "str", and "last" if not '\0' back onto front of filestream f;	*/
/* also moves "pt" back the same number of characters. This routine is	*/
/* is part of a large patch which is best described in the mailing list	*/
/* go see "Re: [Fontanvil-devel] stuck in infinite loop", 2012August22	*/
static void putBack(struct fontparse *fp,AFILE *f,const char *str,
		    char last, char **pt) {
   const char *backpt;

   if (last) {
      (*pt)--;
      if (aungetc(last, f)<0) {
	 fp->alreadycomplained=1;
	 ErrorMsg(2,"Internal Err: Unable to put data back into file\n");
	 return;
      }
   }

   for (backpt=str+strlen(str)-1; backpt >= str; backpt--) {
      if (aungetc(*backpt, f)<0) {
	 fp->alreadycomplained=1;
	 ErrorMsg(2,"Internal Err: Unable to put data back into file\n");
	 break;
      }
   }
   pt -= strlen(str);
}

static void sfnts2tempfile(struct fontparse *fp,AFILE *in,char *line) {
   char *pt;
   int instring=false, firstnibble=true, sofar=0, nibble;
   int complained=false;
   int ch=0;

   fp->sfnts=atmpfile();

   /* first finish off anything in the current line */
   while ((pt=strpbrk(line, "<]")) != NULL) {
      if (*pt==']')
	 goto skip_to_eol;

      instring=true;
      for (++pt; *pt && *pt != '>'; ++pt) {
	 if (isspace(*pt))
	    continue;
	 if (isdigit(*pt))
	    nibble=*pt-'0';
	 else if (*pt >= 'a' && *pt <= 'f')
	    nibble=*pt-'a'+10;
	 else if (*pt >= 'A' && *pt <= 'F')
	    nibble=*pt-'A'+10;
	 else {
	    if (!complained) {
	       ErrorMsg(2,"Invalid hex digit in sfnts array\n");
	       complained=true;
	    }
	    ++pt;
	    continue;
	 }
	 if (firstnibble) {
	    sofar=nibble << 4;
	    firstnibble=false;
	 } else {
	    aputc(sofar|nibble, fp->sfnts);
	    sofar=0;
	    firstnibble=true;
	 }
      }
      if (*pt=='>') {
	 if (aftell(fp->sfnts)&1) {	/* Strings must be contain an even number of bytes */
	    /* But may be padded with a trailing NUL */
	    afseek(fp->sfnts, -1, SEEK_CUR);
	 }
	 ++pt;
	 instring=false;
      }
      line=pt;
   }

   while ((ch=agetc(in)) != EOF) {
      if (ch==']')
	 goto skip_to_eol;
      if (isspace(ch))
	 continue;
      if (!instring && ch=='<') {
	 instring=true;
	 firstnibble=true;
	 sofar=0;
      } else if (!instring) {
	 if (!complained) {
	    ErrorMsg(2,"Invalid character outside of string in sfnts array\n");
	    complained=true;
	 }
      } else if (instring && ch=='>') {
	 if (aftell(fp->sfnts)&1) {	/* Strings must be contain an even number of bytes */
	    /* But may be padded with a trailing NUL */
	    afseek(fp->sfnts, -1, SEEK_CUR);
	 }
	 instring=false;
      } else {
	 if (isdigit(ch))
	    nibble=ch-'0';
	 else if (ch >= 'a' && ch <= 'f')
	    nibble=ch-'a'+10;
	 else if (ch >= 'A' && ch <= 'F')
	    nibble=ch-'A'+10;
	 else {
	    if (!complained) {
	       ErrorMsg(2,"Invalid hex digit in sfnts array\n");
	       complained=true;
	    }
	    continue;
	 }
	 if (firstnibble) {
	    sofar=nibble << 4;
	    firstnibble=false;
	 } else {
	    aputc(sofar|nibble, fp->sfnts);
	    sofar=0;
	    firstnibble=true;
	 }
      }
   }
 skip_to_eol:
   while (ch != EOF && ch != '\n' && ch != '\r')
      ch=agetc(in);
   afseek(fp->sfnts,0,SEEK_SET);
}

static void ParseSimpleEncoding(struct fontparse *fp,char *line) {
   char tok[200], *pt;

   while (*line != '\0' && *line != ']') {
      while (isspace(*line))
	 ++line;
      if (*line==']')
	 break;
      if (*line != '/') {
	 ++line;
	 continue;
      }
      ++line;
      while (isspace(*line))
	 ++line;
      for (pt=tok;
	   !isspace(*line) && *line != '\0' && *line != '/'
	   && *line != ']';) {
	 if (pt<tok+sizeof(tok)-2)
	    *pt++=*line++;
	 else
	    ++line;
      }
      *pt='\0';
      if (fp->simple_enc_pos<256)
	 fp->fd->encoding[fp->simple_enc_pos++]=fastrdup(tok);
   }
   if (*line==']') {
      fp->simpleencoding=false;
      fp->inencoding=false;
   }
}

static void parseline(struct fontparse *fp,char *line,AFILE *in) {
   char buffer[200], *pt, *endtok;

   while (*line==' ' || *line=='\t')
      ++line;
   if (line[0]=='%' && !fp->multiline)
      return;

   if (fp->simpleencoding) {
      ParseSimpleEncoding(fp, line);
      return;
   } else if ((fp->inencoding && strncmp(line, "dup", 3)==0) ||
	      (strncmp(line, "dup ", 4)==0 && isdigit(line[4]) &&
	       strstr(line+strlen(line)-6, " put") != NULL
	       && strchr(line, '/') != NULL)) {
      /* Fontographer's type3 fonts claim to be standard, but then aren't */
      fp->fd->encoding_name=&custom;
      /* Metamorphasis has multiple entries on a line */
      while (strncmp(line, "dup", 3)==0) {
	 char *end;
	 int pos=strtol(line+3, &end, 10);

	 line=end;
	 while (isspace(*line))
	    ++line;
	 if (*line=='/')
	    ++line;
	 for (pt=buffer; !isspace(*line); *pt++=*line++);
	 *pt='\0';
	 if (pos >= 0 && pos<256) {
	    free(fp->fd->encoding[pos]);
	    fp->fd->encoding[pos]=fastrdup(buffer);
	 }
	 while (isspace(*line))
	    ++line;
	 if (strncmp(line, "put", 3)==0)
	    line += 3;
	 while (isspace(*line))
	    ++line;
      }
      return;
   } else if (fp->inencoding && strstr(line, "for") != NULL
	      && strstr(line, "/.notdef") != NULL) {
      /* the T1 spec I've got doesn't allow for this, but I've seen it anyway */
      /* 0 1 255 {1 index exch /.notdef put} for */
      /* 0 1 31 { 1 index exch /.notdef put } bind for */
      int i;

      for (i=0; i<256; ++i)
	 if (fp->fd->encoding[i]==NULL)
	    fp->fd->encoding[i]=fastrdup(".notdef");
      return;
   } else if (fp->inencoding && strstr(line, "Encoding") != NULL
	      && strstr(line, "put") != NULL) {
      /* Saw a type 3 font with lines like "Encoding 1 /_a0 put" */
      char *end;
      int pos;

      while (isspace(*line))
	 ++line;
      if (strncmp(line, "Encoding ", 9)==0) {
	 line += 9;
	 pos=strtol(line, &end, 10);
	 line=end;
	 while (isspace(*line))
	    ++line;
	 if (*line=='/') {
	    ++line;
	    for (pt=buffer; !isspace(*line); *pt++=*line++);
	    *pt='\0';
	    if (pos >= 0 && pos<256)
	       fp->fd->encoding[pos]=fastrdup(buffer);
	 }
      }
      return;
   } else if (fp->insubs) {
      struct pschars *subrs=fp->fd->private->subrs;

      while (isspace(*line))
	 ++line;
      if (strncmp(line, "dup ", 4)==0) {
	 int i;
	 char *ept;

	 for (line += 4; *line==' '; ++line);
	 i=strtol(line, &ept, 10);
	 if (fp->ignore)
	    /* Do Nothing */ ;
	 else if (i<subrs->cnt) {
	    findstring(fp, subrs, i, NULL, ept);
	 } else if (!fp->alreadycomplained) {
	    ErrorMsg(2,"Index too big (must be <%d) \"%s\n", subrs->cnt,
		     rmbinary(line));
	    fp->alreadycomplained=true;
	 }
      } else if (strncmp(line, "readonly put", 12)==0
		 || strncmp(line, "ND", 2)==0
		 || strncmp(line, "|-", 2)==0) {
	 fp->insubs=false;
	 fp->ignore=false;
      } else if (*line=='\n' || *line=='\0') {
	 /* Ignore blank lines */ ;
      } else if (!fp->alreadycomplained) {
	 ErrorMsg(2,"Didn't understand \"%s\" inside subs def'n\n",
		  rmbinary(line));
	 fp->alreadycomplained=true;
      }
   } else if (fp->inchars) {
      struct pschars *chars=fp->fd->chars;

      while (isspace(*line))
	 ++line;
      if (strncmp(line, "end", 3)==0)
	 fp->ignore=fp->inchars=false;
      else if (*line=='\n' || *line=='\0')
	 /* Ignore it */ ;
      else if (*line != '/' || !(isalpha(line[1]) || line[1]=='.')) {
	 ErrorMsg(2,"No name for CharStrings dictionary \"%s\n",
		  rmbinary(line));
	 fp->alreadycomplained=true;
      } else if (fp->ignore) {
	 /* Do Nothing */ ;
      } else if (chars->next >= chars->cnt)
	 ErrorMsg(2,"Too many entries in CharStrings dictionary \"%s\n",
		  rmbinary(line));
      else if (fp->fd->fonttype==42 || fp->fd->fonttype==11
	       || fp->fd->cidfonttype==2)
	 findnumbers(fp, chars, line);
      else {
	 int i=chars->next;
	 char *namestrt=++line;

	 while (isalnum(*line) || *line=='.')
	    ++line;
	 *line='\0';
	 findstring(fp, chars, i, namestrt, line+1);
      }
      return;
   }
   fp->inencoding=0;

   while (isspace(*line))
      ++line;
   endtok=NULL;
   if (*line=='/')
      for (endtok=line+1;
	   !isspace(*endtok) && *endtok != '(' && *endtok != '/'
	   && *endtok != '{' && *endtok != '[' && *endtok != '\0'; ++endtok);

   if (strstr(line, "/shareddict") != NULL && strstr(line, "where") != NULL) {
      fp->infi=fp->inbb=fp->inmetrics=fp->inmetrics2=false;
      fp->inprivate=fp->inblendprivate=fp->inblendfi=false;
      fp->skipping_mbf=true;
      return;
   }

   if (mycmp("Encoding", line+1, endtok)==0 && !fp->doneencoding) {
      if (strstr(endtok, "StandardEncoding") != NULL) {
	 fp->fd->encoding_name=FindOrMakeEncoding("AdobeStandard");
	 setStdEnc(fp->fd->encoding);
      } else if (strstr(endtok, "ISOLatin1Encoding") != NULL) {
	 fp->fd->encoding_name=FindOrMakeEncoding("ISO8859-1");
	 setLatin1Enc(fp->fd->encoding);
      } else {
	 fp->fd->encoding_name=&custom;
	 fp->inencoding=1;
      }
      if (fp->fd->encoding_name==NULL)
	 fp->fd->encoding_name=&custom;
      fp->infi=fp->inprivate=fp->inbb=fp->inmetrics=fp->inmetrics2 =
	 false;
      fp->doneencoding=true;
      while (*endtok==' ' || *endtok=='\t')
	 ++endtok;
      if (*endtok=='[') {	/* It's a literal array */
	 fp->simpleencoding=true;
	 fp->simple_enc_pos=0;
	 ParseSimpleEncoding(fp, endtok+1);
      }
   } else if (mycmp("BoundingBoxes", line+1, endtok)==0) {
      fp->infi=fp->inprivate=fp->inencoding=fp->inmetrics =
	 fp->inmetrics2=false;
      fp->inbb=true;
   } else if (mycmp("Metrics", line+1, endtok)==0) {
      fp->infi=fp->inprivate=fp->inbb=fp->inencoding=fp->inmetrics2 =
	 false;
      fp->inmetrics=true;
      fp->fd->metrics=calloc(1, sizeof(struct psdict));
      fp->fd->metrics->cnt=strtol(endtok, NULL, 10);
      fp->fd->metrics->keys=malloc(fp->fd->metrics->cnt * sizeof(char *));
      fp->fd->metrics->values=malloc(fp->fd->metrics->cnt * sizeof(char *));
   } else if (strstr(line, "/Private") != NULL
	      && strstr(line, "/Blend") != NULL) {
      fp->infi=fp->inbb=fp->inmetrics=fp->inmetrics2=false;
      fp->inprivate=fp->inblendprivate=fp->inblendfi=false;
      fp->inblendprivate=1;
      fp->fd->blendprivate=calloc(1, sizeof(struct psdict));
      InitDict(fp->fd->blendprivate, line);
      return;
   } else if (strstr(line, "/FontInfo") != NULL
	      && strstr(line, "/Blend") != NULL) {
      fp->infi=fp->inbb=fp->inmetrics=fp->inmetrics2=false;
      fp->inprivate=fp->inblendprivate=fp->inblendfi=false;
      fp->inblendfi=1;
      fp->fd->blendfontinfo=calloc(1, sizeof(struct psdict));
      InitDict(fp->fd->blendfontinfo, line);
      return;
   } else if (fp->infi) {
      if (fp->multiline) {
	 ContinueValue(fp, NULL, line);
	 return;
      }
      if (endtok==NULL
	  && (strncmp(line, "end", 3)==0 || strncmp(line, ">>", 2)==0)) {
	 fp->infi=0;
	 return;
      } else if (endtok==NULL)
	 return;
      if (mycmp("version", line+1, endtok)==0) {
	 free(fp->fd->fontinfo->version);
	 fp->fd->fontinfo->version=getstring(endtok, in);
      } else if (mycmp("Notice", line+1, endtok)==0) {
	 free(fp->fd->fontinfo->notice);
	 fp->fd->fontinfo->notice=getstring(endtok, in);
      } else if (mycmp("Copyright", line+1, endtok)==0) {	/* cff spec allows for copyright and notice */
	 free(fp->fd->fontinfo->notice);
	 fp->fd->fontinfo->notice=getstring(endtok, in);
      } else if (mycmp("FullName", line+1, endtok)==0) {
	 if (fp->fd->fontinfo->fullname==NULL)
	    fp->fd->fontinfo->fullname=getstring(endtok, in);
	 else
	    free(getstring(endtok, in));
      } else if (mycmp("FamilyName", line+1, endtok)==0) {
	 free(fp->fd->fontinfo->familyname);
	 fp->fd->fontinfo->familyname=getstring(endtok, in);
      } else if (mycmp("Weight", line+1, endtok)==0) {
	 free(fp->fd->fontinfo->weight);
	 fp->fd->fontinfo->weight=getstring(endtok, in);
      } else if (mycmp("ItalicAngle", line+1, endtok)==0)
	 fp->fd->fontinfo->italicangle=strtod(endtok, NULL);
      else if (mycmp("UnderlinePosition", line+1, endtok)==0)
	 fp->fd->fontinfo->underlineposition=strtod(endtok, NULL);
      else if (mycmp("UnderlineThickness", line+1, endtok)==0)
	 fp->fd->fontinfo->underlinethickness=strtod(endtok, NULL);
      else if (mycmp("isFixedPitch", line+1, endtok)==0)
	 fp->fd->fontinfo->isfixedpitch=getbool(endtok);
      else if (mycmp("em", line+1, endtok)==0)
	 fp->fd->fontinfo->em=strtol(endtok, NULL, 10);
      else if (mycmp("ascent", line+1, endtok)==0)
	 fp->fd->fontinfo->ascent=strtol(endtok, NULL, 10);
      else if (mycmp("descent", line+1, endtok)==0)
	 fp->fd->fontinfo->descent=strtol(endtok, NULL, 10);
      else if (mycmp("FSType", line+1, endtok)==0)
	 fp->fd->fontinfo->fstype=strtol(endtok, NULL, 10);
      else if (mycmp("BlendDesignPositions", line+1, endtok)==0) {
	 fp->pending_parse=&fp->fd->fontinfo->blenddesignpositions;
	 AddValue(fp, NULL, line, endtok);
      } else if (mycmp("BlendDesignMap", line+1, endtok)==0) {
	 fp->pending_parse=&fp->fd->fontinfo->blenddesignmap;
	 AddValue(fp, NULL, line, endtok);
      } else if (mycmp("BlendAxisTypes", line+1, endtok)==0) {
	 fp->pending_parse=&fp->fd->fontinfo->blendaxistypes;
	 AddValue(fp, NULL, line, endtok);
      } else if (!fp->alreadycomplained) {
	 ErrorMsg(2,"Didn't understand \"%s\" in font info\n", rmbinary(line));
	 fp->alreadycomplained=true;
      }
   } else if (fp->inblend) {
      if (endtok==NULL) {
	 if (*line != '/' && strstr(line, "end") != NULL)
	    fp->inblend=false;
	 return;
      }
      /* Ignore anything in the blend dict defn */
   } else if (fp->inblendprivate || fp->inblendfi) {
      struct psdict *subdict =
	 fp->inblendfi?fp->fd->blendfontinfo:fp->fd->blendprivate;
      if (fp->multiline) {
	 ContinueValue(fp, subdict, line);
	 return;
      } else if (endtok==NULL) {
	 if (*line != '/' && strstr(line, "end") != NULL) {
	    fp->inblendprivate=fp->inblendfi=false;
	    fp->inprivate=true;
	 }
	 return;
      } else
	 AddValue(fp, subdict, line, endtok);
   } else if (fp->inprivate) {
      if (strstr(line, "/CharStrings") != NULL
	  && strstr(line, "dict") != NULL) {
	 if (fp->fd->chars->next==0) {
	    InitChars(fp->fd->chars, line);
	    fp->ignore=false;
	 } else {
	    fp->ignore=true;
	    ErrorMsg(2,"Ignoring duplicate /CharStrings entry\n");
	 }
	 fp->inchars=1;
	 fp->insubs=0;
	 return;
      } else if (strstr(line, "/Subrs") != NULL) {
	 if (fp->fd->private->subrs->next>0) {
	    fp->ignore=true;
	    ErrorMsg(2,"Ignoring duplicate /Subrs entry\n");
	 } else {
	    InitChars(fp->fd->private->subrs, line);
	    fp->ignore=false;
	 }
	 fp->insubs=1;
	 fp->inchars=0;
	 return;
      } else if (fp->multiline) {
	 ContinueValue(fp, fp->fd->private->private, line);
	 return;
      }
      if (endtok==NULL) {
	 char *pt=line;

	 if (*pt != '/')
	    while ((pt=strstr(pt, "end")) != NULL) {
	       if (fp->inchars)
		  fp->inchars=false;
	       else
		  fp->inprivate=false;
	       pt += 3;
	    }
	 return;
      }
      if (mycmp("ND", line+1, endtok)==0
	  || mycmp("|-", line+1, endtok)==0
	  || mycmp("NP", line+1, endtok)==0
	  || mycmp("|", line+1, endtok)==0
	  || mycmp("RD", line+1, endtok)==0
	  || mycmp("-|", line+1, endtok)==0
	  || mycmp("password", line+1, endtok)==0
	  || mycmp("MinFeature", line+1, endtok)==0)
	 /* These conveigh no information, but are required */ ;
      else if (mycmp("UniqueID", line+1, endtok)==0) {
	 if (fp->fd->uniqueid==0)
	    fp->fd->uniqueid=strtol(endtok, NULL, 10);
      } else {
	 if (mycmp("lenIV", line+1, endtok)==0)
	    fp->fd->private->leniv=strtol(endtok, NULL, 10);	/* We need this value */
	 AddValue(fp, fp->fd->private->private, line, endtok);
      }
   } else if (fp->incidsysteminfo) {
      if (endtok==NULL && strncmp(line, "end", 3)==0) {
	 fp->incidsysteminfo=0;
	 return;
      } else if (endtok==NULL)
	 return;
      if (mycmp("Registry", line+1, endtok)==0) {
	 free(fp->fd->registry);
	 fp->fd->registry=getstring(endtok, in);
      } else if (mycmp("Ordering", line+1, endtok)==0) {
	 free(fp->fd->ordering);
	 fp->fd->ordering=getstring(endtok, in);
      } else if (mycmp("Supplement", line+1, endtok)==0)	/* cff spec allows for copyright and notice */
	 fp->fd->supplement=strtol(endtok, NULL, 0);
   } else {
      if (strstr(line, "/Private") != NULL
	  && (strstr(line, "dict") != NULL || strstr(line, "<<") != NULL)) {
	 fp->infi=fp->inbb=fp->inmetrics=fp->inmetrics2=false;
	 fp->inprivate=fp->inblendprivate=fp->inblendfi=false;
	 if (strstr(line, "/Blend") != NULL) {
	    fp->inblendprivate=1;
	    fp->fd->blendprivate=calloc(1, sizeof(struct psdict));
	    InitDict(fp->fd->blendprivate, line);
	 } else {
	    fp->inprivate=1;
	    InitDict(fp->fd->private->private, line);
	 }
	 return;
      } else if (strstr(line, "/FontInfo") != NULL
		 && (strstr(line, "dict") != NULL
		     || strstr(line, "<<") != NULL)) {
	 fp->inprivate=fp->inbb=fp->inmetrics=fp->inmetrics2=false;
	 fp->infi=fp->inblendprivate=fp->inblendfi=false;
	 if (strstr(line, "/Blend") != NULL) {
	    fp->inblendfi=1;
	    fp->fd->blendfontinfo=calloc(1, sizeof(struct psdict));
	    InitDict(fp->fd->blendfontinfo, line);
	 } else {
	    if (!strstr(line, "end"))
	       fp->infi=1;
	 }
	 return;
      } else if (strstr(line, "/Blend") != NULL
		 && strstr(line, "dict") != NULL) {
	 fp->inprivate=fp->inbb=fp->inmetrics=fp->inmetrics2=false;
	 fp->infi=fp->inblendprivate=fp->inblendfi=false;
	 fp->inblend=true;
	 return;
      } else if (strstr(line, "/sfnts") != NULL && strstr(line, "[") != NULL) {
	 sfnts2tempfile(fp, in, line);
	 return;
      } else if (strstr(line, "/CharStrings") != NULL
		 && strstr(line, "dict") != NULL && fp->fd->fonttype != 3) {
	 if (fp->fd->chars->next==0) {
	    InitChars(fp->fd->chars, line);
	    fp->ignore=false;
	 } else {
	    fp->ignore=true;
	    ErrorMsg(2,"Ignoring duplicate /CharStrings entry\n");
	 }
	 fp->inchars=1;
	 fp->insubs=0;
	 fp->infi=fp->inprivate=fp->inbb=fp->inmetrics =
	    fp->inmetrics2=false;
	 fp->inblendprivate=fp->inblendfi=false;
	 return;
      } else if (mycmp("/CharProcs", line, endtok)==0) {
	 InitCharProcs(fp->fd->charprocs, line);
	 fp->infi=fp->inprivate=fp->inbb=fp->inmetrics =
	    fp->inmetrics2=false;
	 fp->insubs=0;
	 return;
      } else if (strstr(line, "/CIDSystemInfo") != NULL) {
	 fp->incidsysteminfo=1;
	 return;
      } else if (fp->inmetrics) {
	 if (endtok != NULL)
	    AddValue(fp, fp->fd->metrics, line, endtok);
	 return;
      } else if (fp->inbb) {
	 /* Ignore it */ ;
	 return;
      }

      if (fp->multiline) {
	 ContinueValue(fp, NULL, line);
	 return;
      }

      if (endtok==NULL) {
	 if (fp->skipping_mbf);
	 else if (fp->fdindex != -1 && strstr(line, "end") != NULL) {
	    if (++fp->fdindex >= fp->mainfd->fdcnt)
	       fp->fd=fp->mainfd;
	    else
	       fp->fd=fp->mainfd->fds[fp->fdindex];
	 }
	 return;
      }
      if (mycmp("FontName", line+1, endtok)==0) {
	 if (fp->fd->fontname==NULL)
	    fp->fd->fontname=gettoken(endtok);
	 else
	    free(gettoken(endtok));	/* skip it */
      } else if (mycmp("PaintType", line+1, endtok)==0)
	 fp->fd->painttype=strtol(endtok, NULL, 10);
      else if (mycmp("FontType", line+1, endtok)==0)
	 fp->fd->fonttype=strtol(endtok, NULL, 10);
      else if (mycmp("FontMatrix", line+1, endtok)==0) {
	 if (fp->fd->fontmatrix[0]==0)
	    fillrealarray(fp->fd->fontmatrix, endtok, 6);
	 else {
	    double temp[6];

	    fillrealarray(temp, endtok, 6);
	 }
      } else if (mycmp("LanguageLevel", line+1, endtok)==0)
	 fp->fd->languagelevel=strtol(endtok, NULL, 10);
      else if (mycmp("WMode", line+1, endtok)==0)
	 fp->fd->wmode=strtol(endtok, NULL, 10);
      else if (mycmp("FontBBox", line+1, endtok)==0)
	 fillrealarray(fp->fd->fontbb, endtok, 4);
      else if (mycmp("UniqueID", line+1, endtok)==0) {
	 if (fp->fd->uniqueid==0)
	    fp->fd->uniqueid=strtol(endtok, NULL, 10);
      } else if (mycmp("UniqueId", line+1, endtok)==0) {
	 ErrorMsg(2,"This font contains a \"UniqueId\" variable, but the correct name for it is\n\t\"UniqueID\" (postscript is case concious)\n");
	 if (fp->fd->uniqueid==0)
	    fp->fd->uniqueid=strtol(endtok, NULL, 10);
      } else if (mycmp("XUID", line+1, endtok)==0) {
	 if (fp->fd->xuid[0]==0)
	    fillintarray(fp->fd->xuid, endtok, 20);
      } else if (mycmp("StrokeWidth", line+1, endtok)==0)
	 fp->fd->strokewidth=strtod(endtok, NULL);
      else if (mycmp("WeightVector", line+1, endtok)==0) {
	 if (fp->fd->weightvector==NULL) {
	    fp->pending_parse=&fp->fd->weightvector;
	    AddValue(fp, NULL, line, endtok);
	 }
      } else if (mycmp("$Blend", line+1, endtok)==0) {
	 fp->pending_parse=&fp->fd->blendfunc;
	 AddValue(fp, NULL, line, endtok);
      } else if (strstr(line, "/NormalizeDesignVector") != NULL) {
	 fp->pending_parse=&fp->fd->ndv;
	 AddValue(fp, NULL, line, endtok);
      } else if (strstr(line, "/ConvertDesignVector") != NULL) {
	 fp->pending_parse=&fp->fd->cdv;
	 AddValue(fp, NULL, line, endtok);
      } else if (mycmp("BuildChar", line+1, endtok)==0)
	 /* Do Nothing */ ;
      else if (mycmp("BuildGlyph", line+1, endtok)==0)
	 /* Do Nothing */ ;
      else if (mycmp("CIDFontName", line+1, endtok)==0) {
	 free(fp->fd->cidfontname);
	 fp->fd->cidfontname=gettoken(endtok);
      } else if (mycmp("CIDFontVersion", line+1, endtok)==0) {
	 fp->fd->cidversion=strtod(endtok, NULL);
      } else if (mycmp("CIDFontType", line+1, endtok)==0)
	 fp->fd->cidfonttype=strtol(endtok, NULL, 10);
      else if (mycmp("UIDBase", line+1, endtok)==0)
	 fp->fd->uniqueid=strtol(endtok, NULL, 10);
      else if (mycmp("CIDMapOffset", line+1, endtok)==0)
	 fp->fd->mapoffset=strtol(endtok, NULL, 10);
      else if (mycmp("FDBytes", line+1, endtok)==0)
	 fp->fd->fdbytes=strtol(endtok, NULL, 10);
      else if (mycmp("GDBytes", line+1, endtok)==0)
	 fp->fd->gdbytes=strtol(endtok, NULL, 10);
      else if (mycmp("CIDCount", line+1, endtok)==0)
	 fp->fd->cidcnt=strtol(endtok, NULL, 10);
      else if (mycmp("FDArray", line+1, endtok)==0) {
	 int i;

	 fp->mainfd=fp->fd;
	 fp->fd->fdcnt=strtol(endtok, NULL, 10);
	 fp->fd->fds=calloc(fp->fd->fdcnt, sizeof(struct fontdict *));
	 for (i=0; i<fp->fd->fdcnt; ++i)
	    fp->fd->fds[i]=MakeEmptyFont();
	 fp->fdindex=0;
	 fp->fd=fp->fd->fds[0];
      } else if (mycmp("FontSetInit", line+1, endtok)==0) {
	 fp->iscff=true;
	 fp->iscid=false;
      } else if (mycmp("CIDInit", line+1, endtok)==0) {
	 fp->iscid=true;
	 fp->iscff=false;
      } else if (fp->skipping_mbf) {	/* Skip over the makeblendedfont defn in a multimaster font */
	 /* Do Nothing */
      } else if (!fp->alreadycomplained) {
	 ErrorMsg(2,"Didn't understand \"%s\" in blended font defn\n",
		  rmbinary(line));
	 fp->alreadycomplained=true;
      }
   }
}

static void addinfo(struct fontparse *fp,char *line,char *tok,
		    char *binstart, int binlen, AFILE *in) {
   char *pt;

   decodestr((unsigned char *) binstart, binlen);
   binstart += fp->fd->private->leniv;
   binlen -= fp->fd->private->leniv;
   if (binlen<0) {
      ErrorMsg(2,"Bad CharString. Does not include lenIV bytes.\n");
      return;
   }

 retry:
   if (fp->insubs) {
      struct pschars *chars =
	 /*fp->insubs?*/
	 fp->fd->private->subrs /*: fp->fd->private->othersubrs */ ;
      while (isspace(*line))
	 ++line;
      if (strncmp(line, "dup ", 4)==0) {
	 int i=strtol(line+4, NULL, 10);

	 if (fp->ignore)
	    /* Do Nothing */ ;
	 else if (i<chars->cnt) {
	    if (chars->values[i] != NULL)
	       ErrorMsg(2,"Duplicate definition of subroutine %d\n", i);
	    chars->lens[i]=binlen;
	    chars->values[i]=malloc(binlen);
	    memcpy(chars->values[i], binstart, binlen);
	    if (i >= chars->next)
	       chars->next=i+1;
	 } else if (!fp->alreadycomplained) {
	    ErrorMsg(2,"Index too big (must be <%d) \"%s\n", chars->cnt,
		     rmbinary(line));
	    fp->alreadycomplained=true;
	 }
      } else if (!fp->alreadycomplained) {
	 ErrorMsg(2,"Didn't understand \"%s\" while adding info to private subroutines\n",
		  rmbinary(line));
	 fp->alreadycomplained=true;
      }
   } else if (fp->inchars) {
      struct pschars *chars=fp->fd->chars;

      if (*tok=='\0')
	 ErrorMsg(2,"No name for CharStrings dictionary \"%s\n",
		  rmbinary(line));
      else if (fp->ignore)
	 /* Do Nothing */ ;
      else if (chars->next >= chars->cnt)
	 ErrorMsg(2,"Too many entries in CharStrings dictionary \"%s\n",
		  rmbinary(line));
      else {
	 int i=chars->next;

	 chars->lens[i]=binlen;
	 chars->keys[i]=fastrdup(tok);
	 chars->values[i]=malloc(binlen);
	 memcpy(chars->values[i], binstart, binlen);
	 ++chars->next;
      }
   } else if (!fp->alreadycomplained) {
      /* Special hacks for known badly formatted fonts */
      if (strstr(line, "/CharStrings") != NULL) {
	 for (pt=line; *pt != '/'; ++pt);
	 pt=strchr(pt+1, '/');
	 if (pt != NULL)
	    *pt='\0';
	 parseline(fp, line, in);
	 if (pt != NULL) {
	    *pt='/';
	    line=pt;
	    goto retry;
	 }
	 return;
      } else if (strstr(line, "/Subrs") != NULL) {
	 /* font is defining glyph on same line as Subrs array,      */
	 /* which we need to parse using parseline;                  */
	 /* remove the binary glyph def and retry parse; then add    */
	 /* the glyph def back and continue parsing binary stuff     */
	 pt=binstart;		/* start at binary, work backwards, find glyph def */
	 while (--pt >= line)
	    if (!strncmp("dup", pt, 3))
	       break;
	 if (pt<line)
	    pt=NULL;
	 if (pt != NULL)
	    *pt='\0';
	 parseline(fp, line, in);
	 if (pt != NULL) {
	    *pt='d';
	    line=pt;
	    goto retry;
	 }
	 return;
      }
      ErrorMsg(2,"Shouldn't be in addinfo \"%s\n", rmbinary(line));
      fp->alreadycomplained=true;
   }
}

/* glorpline (maybe means "glyph" or "parse" line) appears to process	*/
/*  each line of the eexec portion of a Type 1 font file.		*/
/*  Call it on each line of the file in order, in order to update "fp".	*/
/*									*/
/* In the book the token which starts a character description is always */
/*  RD but it's just the name of a subroutine which is defined in the	*/
/*  private diction and it could be anything. in one case it was a	*/
/*  "-|" (hyphen bar) so we can't just look for RD we must be a bit	*/
/*  smarter and figure out what the token is (oh. I see now. it's	*/
/*  allowed to be either one "RD" or "-|", but nothing else right)	*/
/* It's defined as {string currentfile exch readstring pop} so look for	*/
/* that Except that in gsf files we've also got				*/
/* "/-!{string currentfile exch readhexstring pop} readonly def"	*/
/*									*/
/*  NOTE: readhexstring!!!						*/
/* And in files generated by GNU fontutils				*/
static int glorpline(struct fontparse *fp,AFILE *temp,char *rdtok) {
   static char *buffer=NULL,*end;
   char *pt, *binstart;
   int binlen;
   int ch;
   int innum, val=0, inbinary, cnt =
      0, inr, wasspace, nownum, nowr, nowspace, sptok;
   char *rdline="{string currentfile exch readstring pop}", *rpt;
   char *rdline2="{string currentfile exch readhexstring pop}";
   char *tokpt=NULL, *rdpt;
   char temptok[255];
   int intok;
   int inPrivate=0, inSubrs=0;
   int wasminus=false, isminus, nibble=0, firstnibble=true, inhex;
   int willbehex=false;

   ch=agetc(temp);
   if (ch==EOF)
      return (0);
   aungetc(ch, temp);

   if (buffer==NULL) {
      buffer=malloc(3000);
      end=buffer+3000;
   }
   innum=inr=0;
   wasspace=0;
   inbinary=0;
   rpt=NULL;
   rdpt=NULL;
   inhex=0;
   pt=buffer;
   binstart=NULL;
   binlen=0;
   intok=0;
   sptok=0;
   temptok[0]='\0';
   while ((ch=agetc(temp)) != EOF) {
      if (pt >= end) {
	 char *old=buffer;
	 int len=(end-buffer)+2000;

	 buffer=realloc(buffer, len);
	 end=buffer+len;
	 pt=buffer+(pt-old);
	 if (binstart != NULL)
	    binstart=buffer+(binstart-old);
      }
      *pt++=ch;
      isminus=ch=='-' && wasspace;
      nownum=nowspace=nowr=0;
      if (rpt != NULL && ch != *rpt && ch=='h' && rpt-rdline>25
	  && rpt-rdline<30 && rdline2[rpt-rdline]=='h') {
	 rpt=rdline2+(rpt-rdline);
	 willbehex=true;
      }
      if (inbinary) {
	 if (--cnt==0)
	    inbinary=0;
      } else if (inhex) {
	 if (ishexdigit(ch)) {
	    int h;

	    if (isdigit(ch))
	       h=ch-'0';
	    else if (ch >= 'a' && ch <= 'f')
	       h=ch-'a'+10;
	    else
	       h=ch-'A'+10;
	    if (firstnibble) {
	       nibble=h;
	       --pt;
	    } else {
	       pt[-1]=(nibble << 4)|h;
	       if (--cnt==0)
		  inbinary=inhex=0;
	    }
	    firstnibble=!firstnibble;
	 } else {
	    --pt;
	    /* skip everything not hex */
	 }
      } else if (ch=='/') {
	 intok=1;
	 tokpt=temptok;
      } else if (intok && !isspace(ch) && ch != '{' && ch != '[') {
	 *tokpt++=ch;
      } else if ((intok || sptok) && (ch=='{' || ch=='[')) {
	 *tokpt='\0';
	 rpt=rdline+1;
	 intok=sptok=0;
      } else if (intok) {
	 *tokpt='\0';
	 intok=0;
	 sptok=1;
	 /* we've just read a name; ensure that we don't have two lines */
	 /* munged together that cause line-by-line parsing to fail     */
	 if (!strcmp("Private", temptok)) {
	    inPrivate=1;
	 } else if (!strcmp("Subrs", temptok)) {
	    inSubrs=1;
	    if (inPrivate) {
	       putBack(fp, temp, temptok, ch, &pt);
	       putBack(fp, temp, "/", '\0', &pt);	/* starts /Subrs token */
	       break;
	    }
	 } else if (!strcmp("CharStrings", temptok)) {
	    if (fp->insubs) {	/* break CharStrings onto a seperate line */
	       putBack(fp, temp, temptok, ch, &pt);
	       putBack(fp, temp, "", '/', &pt);
	       fp->insubs=0;
	       break;
	    }
	 }
      } else if (sptok && isspace(ch)) {
	 nowspace=1;
	 if (ch=='\n' || ch=='\r')
	    break;
      } else if (sptok && !isdigit(ch))
	 sptok=0;
      else if (rpt != NULL && ch==*rpt) {
	 if (*++rpt=='\0') {
	    /* it matched the character definition string so this is the */
	    /*  token we want to search for */
	    strcpy(rdtok, temptok);
	    fp->useshexstrings=willbehex;
	    rpt=NULL;
	 }
      } else if (rpt != NULL && ch==' ') {
	 /* Extra spaces are ok */
      } else if (rpt != NULL) {
	 rpt=NULL;
	 willbehex=false;
      } else if (isdigit(ch)) {
	 sptok=0;
	 nownum=1;
	 if (innum)
	    val=10*val+ch-'0';
	 else
	    val=ch-'0';
      } else if (isspace(ch)) {
	 nowspace=1;
	 if (ch=='\n' || ch=='\r')
	    break;
	 if (inSubrs && matchFromBack(pt-2, "array", pt-buffer-1))
	    break;		/* Subrs may be on same line with first RD def -- seperate them */
      } else if (wasspace && ch==*rdtok) {
	 nowr=1;
	 fp->useshexstrings=willbehex;
	 rdpt=rdtok+1;
      } else if (wasspace && ch=='-') {	/* fonts produced by type1fix seem to define both "RD" and "-|" which confused me. so just respond to either */
	 nowr=1;
	 fp->useshexstrings=false;
	 rdpt="|";
      } else if (wasspace && ch=='R') {	/* fonts produced by type1fix seem to define both "RD" and "-|" which confused me. so just respond to either */
	 nowr=1;
	 fp->useshexstrings=false;
	 rdpt="D";
      } else if (inr && ch==*rdpt) {
	 if (*++rdpt=='\0') {
	    ch=agetc(temp);
	    *pt++=ch;
	    if (isspace(ch) && val != 0) {
	       inhex=fp->useshexstrings;
	       inbinary=!fp->useshexstrings;
	       firstnibble=true;
	       cnt=val;
	       binstart=pt;
	       binlen=val;
	    }
	 } else
	    nowr=1;
      } else if (wasminus && ch=='!') {
	 ch=agetc(temp);
	 *pt++=ch;
	 if (isspace(ch) && val != 0) {
	    inhex=1;
	    cnt=val;
	    binstart=pt;
	    binlen=val;
	    firstnibble=true;
	 }
      }
      innum=nownum;
      wasspace=nowspace;
      inr=nowr;
      wasminus=isminus;
   }				/* end while */
   *pt='\0';
   if (binstart==NULL) {
      parseline(fp, buffer, temp);
   } else {
      addinfo(fp, buffer, temptok, binstart, binlen, temp);
   }
   return (1);
}

static int nrandombytes[4];

#define EODMARKLEN	16

#define bgetc(extra,in)	(*(extra)=='\0'?agetc(in):(unsigned char ) *(extra)++ )

static void decrypteexec(AFILE *in,AFILE *temp,int hassectionheads,
			 char *extra) {
   int ch1, ch2, ch3, ch4, binary;
   int zcnt;
   unsigned char zeros[EODMARKLEN+6+1];
   int sect_len=0x7fffffff;

   if (extra==(void *) 5)
      extra="";

   /* The PLRM defines white space to include form-feed and null. The t1_spec */
   /*  does not. The t1_spec wins here. Someone gave me a font which began */
   /*  with a formfeed and that was part of the encrypted body */
   while ((ch1=bgetc(extra, in)) != EOF
	  && (ch1==' ' || ch1=='\t' || ch1=='\n' || ch1=='\r'));
   if (ch1==0200 && hassectionheads) {
      /* skip the 6 byte section header in pfb files that follows eexec */
      ch1=bgetc(extra, in);
      sect_len=bgetc(extra, in);
      sect_len |= bgetc(extra, in) << 8;
      sect_len |= bgetc(extra, in) << 16;
      sect_len |= bgetc(extra, in) << 24;
      sect_len -= 3;
      ch1=bgetc(extra, in);
   }
   ch2=bgetc(extra, in);
   ch3=bgetc(extra, in);
   ch4=bgetc(extra, in);
   binary=0;
   if (ch1<'0' || (ch1>'9' && ch1<'A') || (ch1>'F' && ch1<'a')
       || (ch1>'f') || ch2<'0' || (ch2>'9' && ch2<'A') || (ch2>'F'
								   && ch2 <
								   'a')
       || (ch2>'f') || ch3<'0' || (ch3>'9' && ch3<'A') || (ch3>'F'
								   && ch3 <
								   'a')
       || (ch3>'f') || ch4<'0' || (ch4>'9' && ch4<'A') || (ch4>'F'
								   && ch4 <
								   'a')
       || (ch4>'f'))
      binary=1;
   if (ch1==EOF || ch2==EOF || ch3==EOF || ch4==EOF) {
      return;
   }

   initcode();
   if (binary) {
      nrandombytes[0]=decode(ch1);
      nrandombytes[1]=decode(ch2);
      nrandombytes[2]=decode(ch3);
      nrandombytes[3]=decode(ch4);
      zcnt=0;
      while ((ch1=bgetc(extra, in)) != EOF) {
	 --sect_len;
	 if (hassectionheads) {
	    if (sect_len==0 && ch1==0200) {
	       ch1=bgetc(extra, in);
	       sect_len=bgetc(extra, in);
	       sect_len |= bgetc(extra, in) << 8;
	       sect_len |= bgetc(extra, in) << 16;
	       sect_len |= bgetc(extra, in) << 24;
	       sect_len += 1;
	       if (ch1=='\1')
		  break;
	    } else {
	       dumpzeros(temp, zeros, zcnt);
	       zcnt=0;
	       aputc(decode(ch1), temp);
	    }
	 } else {
	    if (ch1=='0')
	       ++zcnt;
	    else {
	       dumpzeros(temp, zeros, zcnt);
	       zcnt=0;
	    }
	    if (zcnt>EODMARKLEN)
	       break;
	    if (zcnt==0)
	       aputc(decode(ch1), temp);
	    else
	       zeros[zcnt-1]=decode(ch1);
	 }
      }
   } else {
      nrandombytes[0]=decode(hex(ch1, ch2));
      nrandombytes[1]=decode(hex(ch3, ch4));
      ch1=bgetc(extra, in);
      ch2=bgetc(extra, in);
      ch3=bgetc(extra, in);
      ch4=bgetc(extra, in);
      nrandombytes[2]=decode(hex(ch1, ch2));
      nrandombytes[3]=decode(hex(ch3, ch4));
      zcnt=0;
      while ((ch1=bgetc(extra, in)) != EOF) {
	 while (ch1 != EOF && isspace(ch1))
	    ch1=bgetc(extra, in);
	 while ((ch2=bgetc(extra, in)) != EOF && isspace(ch2));
	 if (ch1=='0' && ch2=='0')
	    ++zcnt;
	 else {
	    dumpzeros(temp, zeros, zcnt);
	    zcnt=0;
	 }
	 if (zcnt>EODMARKLEN)
	    break;
	 if (zcnt==0)
	    aputc(decode(hex(ch1, ch2)), temp);
	 else
	    zeros[zcnt-1]=decode(hex(ch1, ch2));
      }
   }
   while ((ch1=bgetc(extra, in))=='0' || isspace(ch1));
   if (ch1 != EOF)
      aungetc(ch1, in);
}

static void decryptagain(struct fontparse *fp,AFILE *temp,char *rdtok) {
   while (glorpline(fp, temp, rdtok));
}

static void parsetype3(struct fontparse *fp,AFILE *in) {
   PSFontInterpretPS(in, fp->fd->charprocs, fp->fd->encoding);
}

static unsigned char *readt1str(AFILE *temp,int offset,int len,int leniv) {
   int i;
   unsigned char *str, *pt;
   unsigned short r=4330;
   unsigned char plain, cypher;

   /* The CID spec doesn't mention this, but the type 1 strings are all */
   /*  eexec encrupted (with the nested encryption). Remember leniv varies */
   /*  from fd to fd (potentially) */
   /* I'm told (by Ian Kemmish) that leniv==-1 => no eexec encryption */

   afseek(temp, offset, SEEK_SET);
   if (leniv<0) {
      str=pt=malloc(len+1);
      for (i=0; i<len; ++i)
	 *pt++=agetc(temp);
   } else {
      for (i=0; i<leniv; ++i) {
	 cypher=agetc(temp);
	 plain=(cypher ^ (r >> 8));
	 r=(cypher+r)*c1+c2;
      }
      str=pt=malloc(len-leniv+1);
      for (; i<len; ++i) {
	 cypher=agetc(temp);
	 plain=(cypher ^ (r >> 8));
	 r=(cypher+r)*c1+c2;
	 *pt++=plain;
      }
   }
   *pt='\0';
   return (str);
}

static void figurecids(struct fontparse *fp,AFILE *temp) {
   struct fontdict *fd=fp->mainfd;
   int i, j, k, val;
   int *offsets;
   int cidcnt=fd->cidcnt;
   int leniv;

   /* Some cid formats don't have any of these */

   fd->cidstrs=malloc(cidcnt * sizeof(uint8_t *));
   fd->cidlens=malloc(cidcnt * sizeof(int16_t));
   fd->cidfds=malloc((cidcnt+1)*sizeof(int16_t));
   offsets=malloc((cidcnt+1)*sizeof(int));

   afseek(temp, fd->mapoffset, SEEK_SET);
   for (i=0; i <= fd->cidcnt; ++i) {
      for (j=val=0; j<fd->fdbytes; ++j)
	 val=(val << 8)+agetc(temp);
      if (val >= fd->fdcnt && val != 255) {	/* 255 is a special mark */
	 ErrorMsg(2,"Invalid FD (%d) assigned to CID %d.\n", val, i);
	 val=0;
      }
      fd->cidfds[i]=val;
      for (j=val=0; j<fd->gdbytes; ++j)
	 val=(val << 8)+agetc(temp);
      offsets[i]=val;
      if (i != 0) {
	 fd->cidlens[i-1]=offsets[i]-offsets[i-1];
	 if (fd->cidlens[i-1]<0) {
	    ErrorMsg(2,"Bad CID offset for CID %d\n", i-1);
	    fd->cidlens[i-1]=0;
	 }
      }
   }

   for (i=0; i<fd->cidcnt; ++i) {
      if (fd->cidlens[i]==0)
	 fd->cidstrs[i]=NULL;
      else {
	 fd->cidstrs[i]=readt1str(temp, offsets[i], fd->cidlens[i],
				    fd->fds[fd->cidfds[i]]->private->leniv);
	 fd->cidlens[i] -= fd->fds[fd->cidfds[i]]->private->leniv;
      }
   }
   free(offsets);

   for (k=0; k<fd->fdcnt; ++k) {
      struct private *private=fd->fds[k]->private;
      char *ssubroff=PSDictHasEntry(private->private, "SubrMapOffset");
      char *ssdbytes=PSDictHasEntry(private->private, "SDBytes");
      char *ssubrcnt=PSDictHasEntry(private->private, "SubrCount");
      int subroff, sdbytes, subrcnt;

      if (ssubroff != NULL && ssdbytes != NULL && ssubrcnt != NULL &&
	  (subroff=strtol(ssubroff, NULL, 10)) >= 0 &&
	  (sdbytes=strtol(ssdbytes, NULL, 10))>0 &&
	  (subrcnt=strtol(ssubrcnt, NULL, 10))>0) {
	 private->subrs->cnt=subrcnt;
	 private->subrs->values=calloc(subrcnt, sizeof(char *));
	 private->subrs->lens=calloc(subrcnt, sizeof(int));
	 leniv=private->leniv;
	 offsets=malloc((subrcnt+1)*sizeof(int));
	 afseek(temp, subroff, SEEK_SET);
	 for (i=0; i <= subrcnt; ++i) {
	    for (j=val=0; j<sdbytes; ++j)
	       val=(val << 8)+agetc(temp);
	    offsets[i]=val;
	    if (i != 0)
	       private->subrs->lens[i-1]=offsets[i]-offsets[i-1];
	 }
	 for (i=0; i<subrcnt; ++i) {
	    private->subrs->values[i]=readt1str(temp, offsets[i],
						  private->subrs->lens[i],
						  leniv);
	 }
	 private->subrs->next=i;
	 free(offsets);
      }
      PSDictRemoveEntry(private->private, "SubrMapOffset");
      PSDictRemoveEntry(private->private, "SDBytes");
      PSDictRemoveEntry(private->private, "SubrCount");
   }
}

static void dodata(struct fontparse *fp,AFILE *in,AFILE *temp) {
   int binary, cnt, len;
   int ch, ch2;
   char *pt;
   char fontsetname[256];

   while ((ch=agetc(in)) != '(' && ch != '/' && ch != EOF);
   if (ch=='/') {
      /* There appears to be no provision for a hex encoding here */
      /* Why can't they use the same format for routines with the same name? */
      binary=true;
      for (pt=fontsetname; (ch=agetc(in)) != ' ' && ch != EOF;)
	 if (pt<fontsetname+sizeof(fontsetname)-1)
	    *pt++=ch;
      *pt='\0';
   } else {
      if ((ch=agetc(in))=='B' || ch=='b')
	 binary=true;
      else if (ch=='H' || ch=='h')
	 binary=false;
      else {
	 binary=true;		/* Who knows? */
	 ErrorMsg(2,"Failed to parse the StartData command properly\n");
      }
      fontsetname[0]='\0';
      while ((ch=agetc(in)) != ')' && ch != EOF);
   }
   if (afscanf(in, "%d", &len) != 1 || len <= 0) {
      len=0;
      ErrorMsg(2,"Failed to parse the StartData command properly, bad count\n");
   }
   cnt=len;
   while (isspace(ch=agetc(in)));
   aungetc(ch, in);
   for (pt="StartData "; *pt; ++pt)
      agetc(in);			/* And if it didn't match, what could I do about it? */
   if (binary) {
      while (cnt>0) {
	 ch=agetc(in);
	 aputc(ch, temp);
	 --cnt;
      }
   } else {
      while (cnt>0) {
	 /* Hex data are allowed to contain whitespace */
	 while (isspace(ch=agetc(in)));
	 while (isspace(ch2=agetc(in)));
	 ch=hex(ch, ch2);
	 aputc(ch, temp);
	 --cnt;
      }
      if ((ch=agetc(in)) != '>')
	 aungetc(ch, in);
   }
   afseek(temp,0,SEEK_SET);
   if (fp->iscid)
      figurecids(fp, temp);
   else {
      fp->fd->sf=_CFFParse(temp, len, fontsetname);
      fp->fd->wascff=true;
   }
}

static void realdecrypt(struct fontparse *fp,AFILE *in,AFILE *temp) {
   char buffer[1024];		/* 256 was okay, but need this much now when some lines are concatenated */
   int first, hassectionheads;
   char rdtok[20];
   int saw_blend=false;

   strcpy(rdtok, "RD");

   first=1;
   hassectionheads=0;
   while (myfgets(buffer, sizeof(buffer), in) != NULL) {
      if (strstr(buffer, "Blend") != NULL)
	 saw_blend=true;
      if (first && buffer[0]=='\200') {
	 int len=strlen(buffer);

	 hassectionheads=1;
	 fp->fd->wasbinary=true;
	 /* if there were a newline in the section header (in the length word) */
	 /*  we would stop at it, and not read the full header */
	 if (len<6)		/* eat the header */
	    while (len<6) {
	       agetc(in);
	       ++len;
	 } else			/* Otherwise parse anything else on the line */
	    parseline(fp, buffer+6, in);
      } else if (strstr(buffer, "CharProcs") != NULL
		 && strstr(buffer, "begin") != NULL) {
	 parsetype3(fp, in);
	 return;
      } else if (fp->fd->fonttype != 42
		 && strstr(buffer, "CharStrings") != NULL
		 && strstr(buffer, "begin") != NULL) {
	 /* Fontographer uses CharStrings even though they aren't */
	 parsetype3(fp, in);
	 return;
      } else if (!fp->iscid) {
	 if (saw_blend)
	    parseline(fp, buffer, in);
	 /* But if it's a multi master font, don't do the special private hack */
	 else if (strstr(buffer, "/CharStrings") != NULL &&
		  strstr(buffer, "begin") != NULL &&
		  (fp->fd->fonttype != 42 && fp->fd->cidfonttype != 2)) {
	    /* gsf files are not eexec encoded, but the charstrings are encoded */
	    InitChars(fp->fd->chars, buffer);
	    fp->inchars=1;
	    decryptagain(fp, in, rdtok);
	    return;
	 } else if (strstr(buffer, "/Subrs") != NULL
		    && strstr(buffer, "array") != NULL) {
	    /* Same case as above */
	    InitChars(fp->fd->private->subrs, buffer);
	    fp->insubs=1;
	    decryptagain(fp, in, rdtok);
	    return;
	 } else if (strstr(buffer, "/Private") != NULL
		    && (strstr(buffer, "dict") != NULL
			|| strstr(buffer, "<<") != NULL)) {
	    /* files produced by GNU fontutils have some of the same issues */
	    fp->inprivate=1;
	    fp->infi=false;
	    decryptagain(fp, in, rdtok);
	    return;
	 } else
	    parseline(fp, buffer, in);
      } else
	 parseline(fp, buffer, in);
      first=0;
      if (strstr(buffer, "%%BeginData: ") != NULL)
	 break;
      if (strstr(buffer, "currentfile") != NULL
	  && strstr(buffer, "eexec") != NULL) {
	 fp->skipping_mbf=false;
	 break;
      }
   }

   if (strstr(buffer, "%%BeginData: ") != NULL) {
      /* used by both CID fonts and CFF fonts (and chameleons, whatever they are) */
      dodata(fp, in, temp);
   } else if (strstr(buffer, "eexec") != NULL) {
      decrypteexec(in, temp, hassectionheads, strstr(buffer, "eexec")+5);
      afseek(temp,0,SEEK_SET);
      decryptagain(fp, temp, rdtok);
      while (myfgets(buffer, sizeof(buffer), in) != NULL) {
	 if (buffer[0] != '\200' || !hassectionheads)
	    parseline(fp, buffer, in);
      }
   } else if ((fp->fd->fonttype==42 || fp->fd->cidfonttype==2)
	      && fp->sfnts != NULL) {
      fp->fd->sf=_SFReadTTF(fp->sfnts, 0, 0, "<Temp File>", fp->fd);
      afclose(fp->sfnts);
   }
}

FontDict *_ReadPSFont(AFILE *in) {
   AFILE *temp;
   struct fontparse fp;

   temp=atmpfile();
   if (temp==NULL) {
      ErrorMsg(2,"Cannot open a temporary file\n");
      afclose(in);
      return (NULL);
   }

   memset(&fp, '\0', sizeof(fp));
   fp.fd=fp.mainfd=PSMakeEmptyFont();
   fp.fdindex=-1;
   realdecrypt(&fp, in, temp);
   free(fp.vbuf);

   afclose(temp);

   return (fp.fd);
}

FontDict *ReadPSFont(char *fontname) {
   AFILE *in;
   FontDict *fd;

   in=afopen(fontname, "rb");
   if (in==NULL) {
      ErrorMsg(2,"Cannot open %s\n", fontname);
      return (NULL);
   }
   fd=_ReadPSFont(in);
   afclose(in);
   return (fd);
}

void PSCharsFree(struct pschars *chrs) {
   int i;

   if (chrs==NULL)
      return;
   for (i=0; i<chrs->next; ++i) {
      if (chrs->keys != NULL)
	 free(chrs->keys[i]);
      free(chrs->values[i]);
   }
   free(chrs->lens);
   free(chrs->keys);
   free(chrs->values);
   free(chrs);
}

void PSDictFree(struct psdict *dict) {
   int i;

   if (dict==NULL)
      return;
   for (i=0; i<dict->next; ++i) {
      if (dict->keys != NULL)
	 free(dict->keys[i]);
      free(dict->values[i]);
   }
   free(dict->keys);
   free(dict->values);
   free(dict);
}

static void PrivateFree(struct private *prv) {
   PSCharsFree(prv->subrs);
   PSDictFree(prv->private);
   free(prv);
}

static void FontInfoFree(struct fontinfo *fi) {
   free(fi->familyname);
   free(fi->fullname);
   free(fi->notice);
   free(fi->weight);
   free(fi->version);
   free(fi->blenddesignpositions);
   free(fi->blenddesignmap);
   free(fi->blendaxistypes);
   free(fi);
}

void PSFontFree(FontDict * fd) {
   int i;

   if (fd->encoding != NULL)
      for (i=0; i<256; ++i)
	 free(fd->encoding[i]);
   free(fd->fontname);
   free(fd->cidfontname);
   free(fd->registry);
   free(fd->ordering);
   FontInfoFree(fd->fontinfo);
   PSCharsFree(fd->chars);
   PrivateFree(fd->private);
   if (fd->charprocs != NULL) {
      for (i=0; i<fd->charprocs->cnt; ++i)
	 free(fd->charprocs->keys[i]);
      free(fd->charprocs->keys);
      free(fd->charprocs->values);
      free(fd->charprocs);
   }
   if (fd->cidstrs != NULL) {
      for (i=0; i<fd->cidcnt; ++i)
	 free(fd->cidstrs[i]);
      free(fd->cidstrs);
   }
   free(fd->cidlens);
   free(fd->cidfds);
   if (fd->fds != NULL) {
      for (i=0; i<fd->fdcnt; ++i)
	 PSFontFree(fd->fds[i]);
      free(fd->fds);
   }
   free(fd->blendfunc);
   free(fd->weightvector);
   free(fd->cdv);
   free(fd->ndv);

   PSDictFree(fd->blendprivate);
   PSDictFree(fd->blendfontinfo);

   free(fd);
}

char **_NamesReadPostScript(AFILE *ps) {
   char **ret=NULL;
   char buffer[2000], *pt, *end;

   if (ps != NULL) {
      while (afgets(buffer, sizeof(buffer), ps) != NULL) {
	 if (strstr(buffer, "/FontName") != NULL ||
	     strstr(buffer, "/CIDFontName") != NULL) {
	    pt=strstr(buffer, "FontName");
	    pt += strlen("FontName");
	    while (isspace(*pt))
	       ++pt;
	    if (*pt=='/')
	       ++pt;
	    for (end=pt; *end != '\0' && !isspace(*end); ++end);
	    ret=malloc(2*sizeof(char *));
	    ret[0]=copyn(pt, end-pt);
	    ret[1]=NULL;
	    break;
	 } else if (strstr(buffer, "currentfile") != NULL
		    && strstr(buffer, "eexec") != NULL)
	    break;
	 else if (strstr(buffer, "%%BeginData") != NULL)
	    break;
      }
      afclose(ps);
   }
   return (ret);
}

char **NamesReadPostScript(char *filename) {
   return (_NamesReadPostScript(afopen(filename, "rb")));
}
