/* Copyright (C) 2000-2004 by George Williams */
/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "psfont.h"
#include "macfonts.h"
#include <locale.h>
#include <math.h>

struct fontparse {
    FontDict *fd, *mainfd;
    /* always in font data */
    unsigned int infi:1;
    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 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 instring;
    int fdindex;
    char **pending_parse;

    unsigned int alreadycomplained: 1;

    char *vbuf, *vmax, *vpt;
    int depth;
};

static char *copyn(const char *str,long n) {
    char *ret;

    if ( str==NULL )
return( NULL );
    ret = malloc(n+1);
    memcpy(ret,str,n);
    ret[n]='\0';
return( ret );
}

static char *PSDictHasEntry(struct psdict *dict, char *key) {
    int i;

    if ( dict==NULL )
return( NULL );

    for ( i=0; i<dict->next; ++i )
	if ( strcmp(dict->keys[i],key)==0 )
return( dict->values[i] );

return( NULL );
}

static int PSDictRemoveEntry(struct psdict *dict, char *key) {
    int i;

    if ( dict==NULL )
return( false );

    for ( i=0; i<dict->next; ++i )
	if ( strcmp(dict->keys[i],key)==0 )
    break;
    if ( i==dict->next )
return( false );
    free( dict->keys[i]);
    free( dict->values[i] );
    --dict->next;
    while ( i<dict->next ) {
	dict->keys[i] = dict->keys[i+1];
	dict->values[i] = dict->values[i+1];
	++i;
    }

return( true );
}

static void copyenc(char *encoding[256],char *std[256]) {
    int i;
    for ( i=0; i<256; ++i )
	encoding[i] = strdup(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->fontinfo->fstype = -1;
return( ret );
}

static struct fontdict *PSMakeEmptyFont(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->charprocs = calloc(1,sizeof(struct charprocs));
    ret->fontinfo->fstype = -1;
return( ret );
}

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

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

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

    forever {
	while ( *start!='\0' && *start!='(' ) ++start;
	if ( *start=='\0' ) {
	    if ( myfgets(buffer,sizeof(buffer),in)==NULL )
return( strdup(""));
	    start = buffer;
	} else
    break;
    }
    ++start;
    ret = NULL; len = 1;
    forever {
	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 ( myfgets(buffer,sizeof(buffer),in)==NULL )
return( ret );
	start = buffer;
    }
return( ret );
}

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

    while ( *start!='\0' && *start!='/' ) ++start;
    if ( *start=='/' ) ++start;
    for ( end = start; *end!='\0' && !isspace(*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 filldoublearray(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 = NULL;
    }
}

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;
    }
    forever {
	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) {
    if ( ch1>='0' && ch1<='9' )
	ch1 -= '0';
    else if ( ch1>='a' )
	ch1 -= 'a'-10;
    else 
	ch1 -= 'A'-10;
    if ( ch2>='0' && ch2<='9' )
	ch2 -= '0';
    else if ( ch2>='a' )
	ch2 -= 'a'-10;
    else 
	ch2 -= 'A'-10;
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(FILE *out, unsigned char *zeros, int zcnt) {
    while ( --zcnt >= 0 )
	fputc(*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!=')' ) {
	    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;
	subrs->lens[index] = bpt-bs;
	subrs->keys[index] = strdup(nametok);
	subrs->values[index] = malloc(bpt-bs);
	memcpy(subrs->values[index],bs,bpt-bs);
	if ( index>=subrs->next ) subrs->next = index+1;
    }
}

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 );
}

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

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

    if ( fp->inencoding && strncmp(line,"dup",3)==0 ) {
	/* 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 )
		fp->fd->encoding[pos] = strdup(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] = strdup(".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] = strdup(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 ) {
		fprintf( stderr, "Index too big (must be <%d) |%s", 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 ) {
	    fprintf( stderr, "Didn't understand |%s", 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]=='.')) {
	    fprintf( stderr, "No name for CharStrings dictionary |%s", rmbinary(line) );
	    fp->alreadycomplained = true;
	} else if ( fp->ignore ) {
	    /* Do Nothing */;
	} else if ( chars->next>=chars->cnt )
	    fprintf( stderr, "Too many entries in CharStrings dictionary |%s", rmbinary(line) );
	else {
	    int i = chars->next;
	    char *namestrt = ++line;
	    while ( isalnum(*line) || *line=='.' ) ++line;
	    *line = '\0';
	    findstring(fp,chars,i,namestrt,line+1);
	}
    }
    fp->inencoding = 0;

    while ( isspace(*line)) ++line;
    endtok = NULL;
    if ( *line=='/' )
	for ( endtok=line+1; !isspace(*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 ) {
	    setStdEnc(fp->fd->encoding);
	    fp->fd->isadobestd = true;
	} else if ( strstr(endtok,"ISOLatin1Encoding")!=NULL ) {
	    setLatin1Enc(fp->fd->encoding);
	} else {
	    fp->inencoding = 1;
	}
	fp->infi = fp->inprivate = fp->inbb = fp->inmetrics = fp->inmetrics2 = false;
	fp->doneencoding = true;
    } 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 ) {
	    fp->infi=0;
return;
	} else if ( endtok==NULL )
return;
	if ( mycmp("version",line+1,endtok)==0 )
	    fp->fd->fontinfo->version = getstring(endtok,in);
	else if ( mycmp("Notice",line+1,endtok)==0 ) {
	    if ( fp->fd->fontinfo->notice!=NULL )
		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 */
	    if ( fp->fd->fontinfo->notice!=NULL )
		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
		getstring(endtok,in);
	} else if ( mycmp("FamilyName",line+1,endtok)==0 )
	    fp->fd->fontinfo->familyname = getstring(endtok,in);
	else if ( mycmp("Weight",line+1,endtok)==0 )
	    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 ) {
	    fprintf( stderr, "Didn't understand |%s", 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;
		fprintf( stderr, "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;
		fprintf( stderr, "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 )
	    fp->fd->registry = getstring(endtok,in);
	else if ( mycmp("Ordering",line+1,endtok)==0 )
	    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 ) {
	    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 ) {
	    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 {
		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,"/CharStrings")!=NULL && strstr(line,"dict")!=NULL ) {
	    if ( fp->fd->chars->next==0 ) {
		InitChars(fp->fd->chars,line);
		fp->ignore = false;
	    } else {
		fp->ignore = true;
		fprintf( stderr, "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
		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 )
		filldoublearray(fp->fd->fontmatrix,endtok,6);
	    else {
		double temp[6];
		filldoublearray(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 )
	     filldoublearray(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 ) {
	    fprintf(stderr,"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 ) {
	    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 )
	    fp->fd->cidfontname = gettoken(endtok);
	else if ( mycmp("CIDFontVersion",line+1,endtok)==0 ) {
	    fp->fd->cidversion = strtod(endtok,NULL);
#if 0
	    if ( fp->fd->fontinfo->version==NULL ) {
		char temp[40];
		sprintf(temp,"%f", fp->fd->cidversion);
		fp->fd->fontinfo->version = strdup(temp);
	    }
#endif
	} 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 ) {
	    fprintf( stderr, "Didn't understand |%s", rmbinary(line) );
	    fp->alreadycomplained = true;
	}
    }
}

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

    decodestr((unsigned char *) binstart,binlen);
    binstart += fp->fd->private->leniv;
    binlen -= fp->fd->private->leniv;

 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 )
		    fprintf( stderr, "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 ) {
		fprintf( stderr, "Index too big (must be <%d) |%s", chars->cnt, rmbinary(line));
		fp->alreadycomplained = true;
	    }
	} else if ( !fp->alreadycomplained ) {
	    fprintf( stderr, "Didn't understand |%s", rmbinary(line) );
	    fp->alreadycomplained = true;
	}
    } else if ( fp->inchars ) {
	struct pschars *chars = fp->fd->chars;
	if ( *tok=='\0' )
	    fprintf( stderr, "No name for CharStrings dictionary |%s", rmbinary(line) );
	else if ( fp->ignore )
	    /* Do Nothing */;
	else if ( chars->next>=chars->cnt )
	    fprintf( stderr, "Too many entries in CharStrings dictionary |%s", rmbinary(line) );
	else {
	    int i = chars->next;
	    chars->lens[i] = binlen;
	    chars->keys[i] = strdup(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 ) {
	    pt = strstr(line,"dup");
	    if ( pt!=NULL )
		*pt = '\0';
	    parseline(fp,line,in);
	    if ( pt!=NULL ) {
		*pt = 'd';
		line = pt;
 goto retry;
	    }
return;
	}
	fprintf( stderr, "Shouldn't be in addinfo |%s", rmbinary(line) );
	fp->alreadycomplained = true;
    }
}

/* 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 "-|" (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, FILE *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, first;
    int wasminus=false, isminus, nibble, firstnibble, inhex;
    int willbehex = false;

    ch = getc(temp);
    if ( ch==EOF )
return( 0 );
    ungetc(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; first=1;
    temptok[0] = '\0';
    while ( (ch=getc(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 ( isxdigit(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;
	} 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;
	} 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 = getc(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 = getc(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;
	first = 0;
    }
    *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' ? getc(in) : (unsigned char ) *(extra)++ )

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

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

    while ( (ch1=bgetc(extra,in))!=EOF && isspace(ch1));
    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;
		    putc(decode(ch1),temp);
		}
	    } else {
		if ( ch1=='0' ) ++zcnt; else {dumpzeros(temp,zeros,zcnt); zcnt = 0; }
		if ( zcnt>EODMARKLEN )
	break;
		if ( zcnt==0 )
		    putc(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 )
		putc(decode(hex(ch1,ch2)),temp);
	    else
		zeros[zcnt-1] = decode(hex(ch1,ch2));
	}
    }
    while (( ch1=bgetc(extra,in))=='0' || isspace(ch1) );
    if ( ch1!=EOF ) ungetc(ch1,in);
}

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

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

static unsigned char *readt1str(FILE *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 */

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

static void figurecids(struct fontparse *fp,FILE *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(unsigned char *));
    fd->cidlens = malloc(cidcnt*sizeof(short));
    fd->cidfds = malloc((cidcnt+1)*sizeof(short));
    offsets = malloc((cidcnt+1)*sizeof(int));

    fseek(temp,fd->mapoffset,SEEK_SET);
    for ( i=0; i<=fd->cidcnt; ++i ) {
	for ( j=val=0; j<fd->fdbytes; ++j )
	    val = (val<<8) + getc(temp);
	fd->cidfds[i] = val;
	for ( j=val=0; j<fd->gdbytes; ++j )
	    val = (val<<8) + getc(temp);
	offsets[i] = val;
	if ( i!=0 )
	    fd->cidlens[i-1] = offsets[i]-offsets[i-1];
    }

    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));
	    fseek(temp,subroff,SEEK_SET);
	    for ( i=0; i<=subrcnt; ++i ) {
		for ( j=val=0; j<sdbytes; ++j )
		    val = (val<<8) + getc(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, FILE *in, FILE *temp) {
    int binary, cnt, len;
    int ch, ch2;
    char *pt;
    char fontsetname[256];

    while ( (ch=getc(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=getc(in))!=' ' && ch!=EOF; )
	    if ( pt<fontsetname+sizeof(fontsetname)-1 )
		*pt++= ch;
	*pt = '\0';
    } else {
	if ( (ch=getc(in))=='B' || ch=='b' ) binary = true;
	else if ( ch=='H' || ch=='h' ) binary = false;
	else {
	    binary = true;		/* Who knows? */
	    fprintf( stderr, "Failed to parse the StartData command properly\n" );
	}
	fontsetname[0] = '\0';
    }
    while ( (ch=getc(in))!=')' && ch!=EOF );
    if ( fscanf( in, "%d", &len )!=1 || len<=0 ) {
	len = 0;
	fprintf( stderr, "Failed to parse the StartData command properly, bad cnt\n" );
    }
    cnt = len;
    while ( isspace(ch=getc(in)) );
    ungetc(ch,in);
    for ( pt="StartData "; *pt; ++pt )
	getc(in);			/* And if it didn't match, what could I do about it? */
    if ( binary ) {
	while ( cnt>0 ) {
	    ch = getc(in);
	    putc(ch,temp);
	    --cnt;
	}
    } else {
	while ( cnt>0 ) {
	    /* Hex data are allowed to contain whitespace */
	    while ( isspace(ch=getc(in)) );
	    while ( isspace(ch2=getc(in)) );
	    ch = hex(ch,ch2);
	    putc(ch,temp);
	    --cnt;
	}
	if ( (ch=getc(in))!='>' ) ungetc(ch,in);
    }
    rewind(temp);
    if ( fp->iscid )
	figurecids(fp,temp);
    else {
	/*fp->fd->sf = _CFFParse(temp,len,fontsetname);*/
	fp->fd->wascff = true;
    }
}

static void doubledecrypt(struct fontparse *fp,FILE *in, FILE *temp) {
    char buffer[256];
    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' ) {
	    hassectionheads = 1;
	    fp->fd->wasbinary = true;
	    parseline(fp,buffer+6,in);
	} else if ( strstr(buffer,"CharProcs")!=NULL && strstr(buffer,"begin")!=NULL ) {
	    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 ) {
		/* 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 ) {
		/* 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 {
	decrypteexec(in,temp,hassectionheads,strstr(buffer, "eexec")+5);
	rewind(temp);
	decryptagain(fp,temp,rdtok);
	while ( myfgets(buffer,sizeof(buffer),in)!=NULL ) {
	    if ( buffer[0]!='\200' || !hassectionheads )
		parseline(fp,buffer,in);
	}
    }
}

static FontDict *_ReadPSFont(FILE *in) {
    FILE *temp;
    struct fontparse fp;
    char *oldloc;

    temp = tmpfile();
    if ( temp==NULL ) {
	fprintf( stderr, "Cannot open a temporary file\n" );
	fclose(in); 
return(NULL);
    }

    oldloc = setlocale(LC_NUMERIC,"C");
    memset(&fp,'\0',sizeof(fp));
    fp.fd = fp.mainfd = PSMakeEmptyFont();
    fp.fdindex = -1;
    doubledecrypt(&fp,in,temp);
    free(fp.vbuf);
    setlocale(LC_NUMERIC,oldloc);

    fclose(temp);
return( fp.fd );
}


static 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);
}

static 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);
#if 1
    PSDictFree(prv->private);
#else
    PSCharsFree(prv->othersubrs);
    free(prv->minfeature);
    free(prv->nd);
    free(prv->np);
    free(prv->rd);
#endif
    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);
}

static 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);
}

static struct bbglyph *FindAdobeGlyph(PSFONT *psfont,int adobe_enc) {
    char *search_name;
    struct pschars *chars = psfont->temp;
    int i;

    if ( adobe_enc>=256 || adobe_enc<0 )
return( &psfont->glyphs[0] );		/* Return .notdef */
    search_name = AdobeStandardEncoding[adobe_enc];
    for ( i=0; i<chars->cnt; ++i ) {
	if ( strcmp(search_name,chars->keys[i])==0 ) {
	    if ( psfont->glyphs[i].isref )
return( NULL );

return( &psfont->glyphs[i] );
	}
    }

    for ( i=0; i<chars->cnt; ++i ) {
	if ( strcmp(".notdef",chars->keys[i])==0 )
return( &psfont->glyphs[i] );
    }

return( &psfont->glyphs[0] );		/* Return something. font doesn't even have notdef */
}

static void FindBB(PSFONT *psfont, struct bbglyph *bb, unsigned char *type1, int len,
	struct pschars *subrs, char *name ) {
    double stack[50]; int sp=0, v;		/* Type1 stack is about 25 long, Type2 stack is 48 */
    double transient[32];
    struct { double x,y; } current;
    double dx, dy, dx2, dy2, dx3, dy3;
    /* subroutines may be nested to a depth of 10 */
    struct substate { unsigned char *type1; int len; int subnum; } pcstack[11];
    int pcsp=0;
    double pops[30];
    int popsp=0;
    int base, polarity;
    struct pschars *s;
    int first = true;
    int lsidebearing;
    struct bbglyph *bb1, *bb2;
    int offx, offy;

    current.x = current.y = 0;
    while ( len>0 ) {
	if ( sp>48 ) {
	    fprintf( stderr, "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;
	    }
	} 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 ) {
	    v = *type1++;
	    --len;
	    switch ( v ) {
	      case 0: /* dotsection */
		sp = 0;
	      break;
	      case 1: /* vstem3 */	/* specifies three v hints zones at once */
		if ( sp<6 ) fprintf(stderr, "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 */
		sp = 0;
	      break;
	      case 2: /* hstem3 */	/* specifies three h hints zones at once */
		if ( sp<6 ) fprintf(stderr, "Stack underflow on hstem3 in %s\n", name );
		sp = 0;
	      break;
	      case 6: /* seac */	/* build accented characters */
 seac:
		if ( sp<5 ) fprintf(stderr, "Stack underflow on seac in %s\n", name );
		/* stack[0] must be the lsidebearing of the accent. I'm not sure why */
		bb1 = FindAdobeGlyph(psfont,stack[3]);
		bb2 = FindAdobeGlyph(psfont,stack[4]);
		offx = stack[1] - (stack[0]-lsidebearing);
		offy = stack[2];
		if ( bb1==NULL || bb2==NULL )
		    bb->isref = true;
		else {
		    bb->top = bb1->top > bb2->top+offy ? bb1->top : bb2->top+offy;
		    bb->bottom = bb1->bottom < bb2->bottom+offy ? bb1->bottom : bb2->bottom+offy;
		    bb->right = bb1->right > bb2->right+offx ? bb1->right : bb2->right+offx;
		    bb->left = bb1->left < bb2->left+offx ? bb1->left : bb2->left+offx;
		}
		sp = 0;
	      break;
	      case 7: /* sbw */		/* generalized width/sidebearing command */
		if ( sp<4 ) fprintf(stderr, "Stack underflow on sbw in %s\n", name );
		lsidebearing = stack[0];
		/* stack[1] is lsidebearing y (only for vertical writing styles, CJK) */
		bb->hadvance = 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 ) fprintf(stderr, "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 ) fprintf(stderr, "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 ) fprintf(stderr, "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 */
		do {
		    stack[sp] = (rand()/(RAND_MAX-1));
		} while ( stack[sp]==0 || stack[sp]>1 );
		++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 */
		/*  double postscript stack */
		if ( sp<2 || sp < 2+stack[sp-2] ) {
		    fprintf(stderr, "Stack underflow on callothersubr in %s\n", name );
		    sp = 0;
		} else {
		    int tot = stack[sp-2], k;
		    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;
		      } break;
		      case 1: { /* Flex */
		      } break;
		      case 2: {
			/* No op */;
		      } break;
		      case 0:
		      break;
		    }
		    sp = k+1;
		}
	      break;
	      case 20: /* put */
		if ( sp<2 ) fprintf(stderr, "Too few items on stack for put in %s\n", name );
		else if ( stack[sp-1]<0 || stack[sp-1]>=32 ) fprintf(stderr,"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 ) fprintf(stderr, "Too few items on stack for get in %s\n", name );
		else if ( stack[sp-1]<0 || stack[sp-1]>=32 ) fprintf(stderr,"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 )
		    fprintf(stderr, "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 )
			fprintf( stderr, "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 )
			fprintf( stderr, "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 ( sp<2 ) fprintf(stderr, "Stack underflow on setcurrentpoint in %s\n", name );
		else {
		    current.x = stack[0];
		    current.y = stack[1];
		    if ( current.y>bb->top ) bb->top = current.y;
		    if ( current.y<bb->bottom ) bb->bottom = current.y;
		    if ( current.x>bb->right ) bb->right = current.x;
		    if ( current.x<bb->left ) bb->left = current.x;
		}
		sp = 0;
	      break;
	      case 34:	/* hflex */
	      case 35:	/* flex */
	      case 36:	/* hflex1 */
	      case 37:	/* flex1 */
		sp = 0;
	      break;
	      default:
		fprintf( stderr, "Uninterpreted opcode 12,%d in %s\n", v, name );
	      break;
	    }
	} else switch ( v ) {
	  case 1: /* hstem */
	  case 18: /* hstemhm */
	    base = 0;
	    if ( sp&1 ) {
		bb->hadvance = stack[0];
		base=1;
	    }
	    if ( sp-base<2 )
		fprintf(stderr, "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 */
	    sp = 0;
	  break;
	  case 19: /* hintmask */
	  case 20: /* cntrmask */
	    /* If there's anything on the stack treat it as a vstem hint */
	    base = 0;
	  case 3: /* vstem */
	  case 23: /* vstemhm */
	  break;
	  case 14: /* endchar */
	    /* endchar is allowed to terminate processing even within a subroutine */
	    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;
	    }
	    /* 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 ) fprintf(stderr, "Stack underflow on hsbw in %s\n", name );
	    lsidebearing = stack[0];
	    current.x = stack[0];		/* sets the current point too */
	    bb->hadvance = stack[1];
	    sp = 0;
	  break;
	  case 9: /* closepath */
	    sp = 0;
	  break;
	  case 21: /* rmoveto */
	  case 22: /* hmoveto */
	  case 4: /* vmoveto */
	  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<2 ) fprintf(stderr, "Stack underflow on rlineto/rmoveto in %s\n", name );
		    dx = stack[base++];
		    dy = stack[base++];
		} else if ( (v==6 && !(polarity&1)) || (v==7 && (polarity&1)) || v==22 ) {
		    if ( sp<1 ) fprintf(stderr, "Stack underflow on hlineto/hmoveto in %s\n", name );
		    dx = stack[base++];
		} else /*if ( (v==7 && !(parity&1)) || (v==6 && (parity&1) || v==4 )*/ {
		    if ( sp<1 ) fprintf(stderr, "Stack underflow on vlineto/vmoveto in %s\n", name );
		    dy = stack[base++];
		}
		++polarity;
		current.x += dx; current.y += dy;
		if ( first ) {
		    bb->top = bb->bottom = current.y;
		    bb->left = bb->right = current.x;
		    first = false;
		} else {
		    if ( current.y>bb->top ) bb->top = current.y;
		    if ( current.y<bb->bottom ) bb->bottom = current.y;
		    if ( current.x>bb->right ) bb->right = current.x;
		    if ( current.x<bb->left ) bb->left = current.x;
		}
	    }
	    sp = 0;
	  break;
	  case 25: /* rlinecurve */
	    base = 0;
	    while ( sp>base+6 ) {
		current.x += stack[base++]; current.y += stack[base++];
		if ( current.y>bb->top ) bb->top = current.y;
		if ( current.y<bb->bottom ) bb->bottom = current.y;
		if ( current.x>bb->right ) bb->right = current.x;
		if ( current.x<bb->left ) bb->left = current.x;
	    }
	  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 ) {
			fprintf(stderr, "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 ) {
			fprintf(stderr, "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 ) {
			fprintf(stderr, "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 ) {
			fprintf(stderr, "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 ) {
			fprintf(stderr, "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;
		current.x += dx; current.y += dy;
		if ( current.y>bb->top ) bb->top = current.y;
		if ( current.y<bb->bottom ) bb->bottom = current.y;
		if ( current.x>bb->right ) bb->right = current.x;
		if ( current.x<bb->left ) bb->left = current.x;
		current.x += dx2; current.y += dy2;
		if ( current.y>bb->top ) bb->top = current.y;
		if ( current.y<bb->bottom ) bb->bottom = current.y;
		if ( current.x>bb->right ) bb->right = current.x;
		if ( current.x<bb->left ) bb->left = current.x;
		current.x += dx3; current.y += dy3;
		if ( current.y>bb->top ) bb->top = current.y;
		if ( current.y<bb->bottom ) bb->bottom = current.y;
		if ( current.x>bb->right ) bb->right = current.x;
		if ( current.x<bb->left ) bb->left = current.x;
	    }
	    if ( v==24 ) {
		current.x += stack[base++]; current.y += stack[base++];
		if ( current.y>bb->top ) bb->top = current.y;
		if ( current.y<bb->bottom ) bb->bottom = current.y;
		if ( current.x>bb->right ) bb->right = current.x;
		if ( current.x<bb->left ) bb->left = current.x;
	    }
	    sp = 0;
	  break;
	  case 29: /* callgsubr */
	  case 10: /* callsubr */
	    /* stack[sp-1] contains the number of the subroutine to call */
	    if ( sp<1 ) {
		fprintf(stderr, "Stack underflow on callsubr in %s\n", name );
	  break;
	    } else if ( pcsp>10 ) {
		fprintf( stderr, "Too many subroutine calls in %s\n", name );
	  break;
	    }
	    s=subrs;
	    if ( s==NULL || stack[sp-1]>=s->cnt || stack[sp-1]<0 ||
		    s->values[(int) stack[sp-1]]==NULL )
		fprintf(stderr,"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 ) fprintf(stderr, "return when not in subroutine in %s\n", name );
	    else {
		--pcsp;
		type1 = pcstack[pcsp].type1;
		len = pcstack[pcsp].len;
	    }
	  break;
	  default:
	    fprintf( stderr, "Uninterpreted opcode %d in %s\n", v, name );
	  break;
	}
    }
  done:
    if ( pcsp!=0 )
	fprintf(stderr, "end of subroutine reached with no return in %s\n", name );
}


void ParsePfb(FILE *pfb,PSFONT *psfont) {
    FontDict *fd = _ReadPSFont(pfb);
    char *pt;
    int i,j, anyrefs, attempts;

    memset(psfont,0,sizeof(PSFONT));
    if ( fd==NULL || fd->fontname==NULL )
return;
    psfont->glyphcnt = fd->chars->next;
    psfont->glyphs = calloc(psfont->glyphcnt,sizeof(struct bbglyph));
    psfont->temp = fd->chars;
    for ( i=0; i<256; ++i ) {
	for ( j=fd->chars->next-1; j>=0; --j )
	    if ( strcmp(fd->encoding[i],fd->chars->keys[j])==0 )
	break;
	if ( j==-1 )
	    for ( j=fd->chars->next-1; j>0; --j )
		if ( strcmp(".notdef",fd->chars->keys[j])==0 )
	    break;
	psfont->encoding[i] = j;
    }
    psfont->fontname = strdup(fd->fontname);
    if ( fd->fontinfo!=NULL ) {
	psfont->familyname = strdup(fd->fontinfo->familyname);
	psfont->fullname = strdup(fd->fontinfo->fullname);
	psfont->weight = strdup(fd->fontinfo->weight);
	psfont->notice = strdup(fd->fontinfo->notice);
	psfont->version = strdup(fd->fontinfo->version);
	psfont->italicangle = fd->fontinfo->italicangle;
    } else {
	psfont->familyname = strdup(fd->fontname);
	if ( (pt = strchr(psfont->familyname,'-'))!=NULL )
	    *pt = '\0';
	psfont->fullname = strdup(fd->fontname);
	psfont->weight = strdup("Regular");
    }
    psfont->isadobestd = fd->isadobestd;
    psfont->em = 1000;
    if ( fd->fontmatrix[0]!=0 )
	psfont->em = 1.0/fd->fontmatrix[0];
    memcpy(psfont->fbb,fd->fontbb,sizeof(psfont->fbb));

    anyrefs = true;
    for ( i=0; i<psfont->glyphcnt; ++i )
	psfont->glyphs[i].isref = true;
    attempts = 0;
    while ( anyrefs && attempts<psfont->glyphcnt ) {
	anyrefs = false;
	for ( i=0; i<psfont->glyphcnt; ++i ) if ( psfont->glyphs[i].isref ) {
	    psfont->glyphs[i].isref = false;
	    FindBB(psfont,&psfont->glyphs[i],fd->chars->values[i],fd->chars->lens[i],
		    fd->private->subrs, fd->chars->keys[i]);
	    if ( psfont->glyphs[i].isref )
		anyrefs = true;
	    else
		psfont->glyphs[i].glyphname = strdup(fd->chars->keys[i]);
	}
	++attempts;
    }
    for ( i=0; i<psfont->glyphcnt; ++i ) if ( !psfont->glyphs[i].isref ) {
	if ( strcmp(psfont->glyphs[i].glyphname,"X")==0 )
	    psfont->ch = psfont->glyphs[i].top;
	else if ( strcmp(psfont->glyphs[i].glyphname,"x")==0 )
	    psfont->xh = psfont->glyphs[i].top;
	else if ( strcmp(psfont->glyphs[i].glyphname,"l")==0 )
	    psfont->as = psfont->glyphs[i].top;
	else if ( strcmp(psfont->glyphs[i].glyphname,"p")==0 )
	    psfont->ds = psfont->glyphs[i].bottom;
    }
    for ( i=0; i<psfont->glyphcnt; ++i ) if ( psfont->glyphs[i].isref ) {
	-- psfont->glyphcnt;
	memcpy(&psfont->glyphs[i],&psfont->glyphs[i+1],(psfont->glyphcnt-i)*sizeof(struct bbglyph));
    }
    PSFontFree(fd);
}
