Untype1.c program -- a pipeline, more portable version

Kevin O'Gorman kevin at kosman.UUCP
Tue Dec 4 10:59:16 AEST 1990


A little while ago, Chris Sears (sears at dungeon.enet.dec.com) posted a
set of programs to decode eexec-ed type 1 font programs.  I had some
trouble with that version, especially on MSDOS, so I combined some stuff
and made this version, which compiles on MSC 5.1 and 6.00A, as well as
under gcc on my SYSV UNIX.  It should be reasonably portable, since
the machines differ in word-size and endianness.  There are some SYSV-isms,
but just in the names of include files, I think.  Also, see NEED_STRSTR.

This routine also can be used in a pipeline.  Specify - for the standard
input.  Output is always on stdout.

I HAVE been having some trouble with it though -- the resulting
PostScript seems always to get a VMERROR on my LaserWriter Plus, on any
real font.  Ideas?  The result is informative anyway.

I tried sending this to Chris first, but email to the indicated address
does not work.  Chris, thanks for the original.

This is a single source file, not in shar format.  Beware the trailing
signature.

/*
 * untype1.c -- decrypt an Adobe font file encrypted for eexec
 *
 * original by Chris B. Sears (sears at dungeon.enet.dec.com)
 *
 * Modified for portability by Kevin O'Gorman (kosman!kevin), because MSDOS
 * had trouble with intermediate files that were mixed binary and text, and
 * because it's really all one task. (3 Dec 1990)
 *
 * The original was distributed without a copyright notice, and no comment
 * about the status.  I take that to place it in the public domain.  I
 * (Kevin O'Gorman) specifically place my modifications in the public domain
 * -- I assert no rights, and accept no responsibility.
 *
 * As a courtesy, I request that notices of authorship be kept honest and
 * complete (and don't forget your own).
 *
 * usage:
 *	untype1 [-s] inputfile
 *
 * The optional -s switch causes the output to be truncated at the first line
 * containing the token "definefont".  This is useful in conjunction with the
 * shell scripts distributed by Chris Sears.
 *
 * The font-related code assumes that the font is eexec encoded (they almost
 * always are); the copying loop for stuff outside of eexec does not check
 * for font-related things.  Further, the RD and -| tokens are hardwired in.
 *
 * Inputfile may be specified as "-".
 *
 * The decoded result is placed on the standard output.
 *
 * Current status: the results get VMerrors on my LaserWriter Plus, even if
 * I setpacking to true.  The results are nevertheless very informative.
 *
 * Things to do:
 *   1: directly turn a type1 into a type3 font.
 *   2: draw the font characters large, with the hints and control points
 *       made visible.
 *   3: decode printer internal fonts (type 5).
 *   4: strip ^D (from some postscript files) and ^Z (from MSDOS) characters.
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define	SEEK_SET	0

#define TRUE				1
#define FALSE				0

#define EEXEC_SKIP_BYTES	4
#define MAX_ESCAPE		33

int lenIV = 4;			/* CharString salt length */

typedef struct {
	char *command;
	int value;
} Command;

#define	BUFFERSIZE	4096
static int	skip_salt = EEXEC_SKIP_BYTES + 1;
static FILE	*in;
static char	in_buff[BUFFERSIZE], out_buff[BUFFERSIZE];
static int	in_size, in_ptr;
static int	o, obase;
static unsigned short int
		cr, cc1, cc2, er, ec1, ec2;
#define	ER	55665
#define EC1	52845
#define	EC2	22719
#define CR	4330
#define CC1	52845
#define CC2	22719
static int	count, csalt;

Command commands[] = {
	{ "notdefined_c0",	0 },
	{ "hstem",			1 },
	{ "notdefined_c2",	2 },
	{ "vstem",			3 },
	{ "vmoveto",		4 },
	{ "chars_rlineto",	5 },
	{ "hlineto",		6 },
	{ "vlineto",		7 },
	{ "rrcurveto",		8 },
	{ "chars_closepath",9 },
	{ "callsubr",		10 },
	{ "return",			11 },
	{ "escape",			12 },
	{ "hsbw",			13 },
	{ "endchar",		14 },
	{ "notdefined_c15",	15 },
	{ "notdefined_c16",	16 },
	{ "notdefined_c17",	17 },
	{ "notdefined_c18",	18 },
	{ "notdefined_c19",	19 },
	{ "notdefined_c20",	20 },
	{ "chars_rmoveto",	21 },
	{ "hmoveto",		22 },
	{ "notdefined_c23",	23 },
	{ "notdefined_c24",	24 },
	{ "notdefined_c25",	25 },
	{ "notdefined_c26",	26 },
	{ "notdefined_c27",	27 },
	{ "notdefined_c28",	28 },
	{ "notdefined_c29",	29 },
	{ "vhcurveto",		30 },
	{ "hvcurveto",		31 }
};

Command escapes[] = {
	{ "dotsection",		0 },
	{ "vstem3",			1 },
	{ "hstem3",			2 },
	{ "notdefined_e3",	3 },
	{ "notdefined_e4",	4 },
	{ "notdefined_e5",	5 },
	{ "seac",			6 },
	{ "sbw",			7 },
	{ "notdefined_e8",	8 },
	{ "notdefined_e9",	9 },
	{ "notdefined_e10",	10 },
	{ "notdefined_e11",	11 },
	{ "chars_div",		12 },
	{ "notdefined_e13",	13 },
	{ "notdefined_e14",	14 },
	{ "notdefined_e15",	15 },
	{ "callothersubr",	16 },
	{ "chars_pop",		17 },
	{ "notdefined_e18",	18 },
	{ "notdefined_e19",	19 },
	{ "notdefined_e20",	20 },
	{ "notdefined_e21",	21 },
	{ "notdefined_e22",	22 },
	{ "notdefined_e23",	23 },
	{ "notdefined_e24",	24 },
	{ "notdefined_e25",	25 },
	{ "notdefined_e26",	26 },
	{ "notdefined_e27",	27 },
	{ "notdefined_e28",	28 },
	{ "notdefined_e29",	29 },
	{ "notdefined_e30",	30 },
	{ "notdefined_e31",	31 },
	{ "notdefined_e32",	32 },
	{ "setcurrentpoint",33 }
};

unsigned char GetPlain()
{
	if (in_ptr >= in_size) {
		if(fgets(in_buff, BUFSIZ, in) == NULL) {
			fprintf(stderr,"Errror -- end of file.\n");
			exit(1);
		}
		in_size = strlen(in_buff) - 1;
		in_ptr = 0;
	}
	return in_buff[in_ptr++];
}

unsigned int GetHexChar()
{
	unsigned int byte;

	/* skip whitespace */
	do {
		byte = GetPlain();
	} while   ((byte == '\n') || (byte == ' ') || (byte == '\t')
		|| (byte == '\r') || (byte == '\f'));

	/* figure the digit */
	if ( (byte >= '0') && (byte <= '9') ) return (byte - '0');
	if ( (byte >= 'A') && (byte <= 'F') ) return (byte - 'A' + 10);
	if ( (byte >= 'a') && (byte <= 'f') ) return (byte - 'a' + 10);

	/* anything else is an error */
	fprintf(stderr, "Illegal hex digit.\n");
	exit (1);
}

unsigned char
EDeCrypt()
{
	unsigned char ch1, ch2, plain, cipher;

	/*
	 * Decode cipher.
	 */
	do {
		ch1 = GetHexChar();
		ch2 = GetHexChar();
		cipher = (ch1 << 4) + ch2;

		plain = (cipher ^ (er >> 8));
		er = (cipher + er) * ec1 + ec2;
		if (skip_salt) skip_salt--;
	} while (skip_salt);
	
	return plain;
}

unsigned char
CDeCrypt()
{
	unsigned char plain, cypher;
	if (count <= 0) {
		fprintf(stderr,"Reading too many bytes of charstring data.\n");
		exit(1);
	}
	do {
		cypher = EDeCrypt();
		count--;
		plain = (cypher ^ (cr >> 8));
		cr = (cypher + cr) * cc1 + cc2;
		if (csalt) csalt--;
	} while (csalt);
	
	return plain;
}

#ifdef	NEED_STRSTR
char *
strstr(char *string, char *target)
{
	int slen, tlen, i;
	tlen = strlen(target);
	slen = strlen(string);
	for (i=0; i+tlen <= slen; i++) {
		if (!strncmp(target, &string[i],tlen)) return (&string[i]);
	}
	return NULL;
}
#endif

void
out_check()
{
	out_buff[o] = '\0';		/* terminate the string */
					/* mostly serves to help use CodeView */
	if (o-obase > 78) {
		int ptr;
		unsigned char c;
		
		for (ptr=obase+78; ptr>obase; ptr--) {
			c = out_buff[ptr];
			if(c==' ' || c=='\t') break;
		}
		if (ptr-obase < 10) return;
		for (; ptr>obase; ptr--) {
			c = out_buff[ptr];
			if (isalpha(c)) break;
		}
		if (ptr-obase < 10) return;
		for (;; ptr++) {
			c = out_buff[ptr];
			if(c==' ' || c=='\t') break;
		}
		out_buff[ptr] = '\0';
		fputs(out_buff, stdout);
		strcpy(out_buff,"\n\t");
		strcat(out_buff,&out_buff[ptr+1]);
		o = strlen(out_buff);
		obase = -6;
	}
}


void
main(argc, argv)
	int argc;
	char **argv;
{
	unsigned char plainchar;
	char *count_pos;
	int i;
	int line_pos;
	long value;
#define	DATA_STATE	0
#define	CHAR_STATE	1
#define	CINZ_STATE	2
	int state = DATA_STATE;
	unsigned char byte;
	
	if ((argc != 2 && argc != 3)
		|| (argc == 3 && strcmp(argv[1],"-s"))) {
		fprintf(stderr, "Usage: %s [-s] input\n", argv[0]);
		exit(0);
	}

	if (strcmp(argv[argc-1], "-")) {
		if ((in = fopen(argv[argc-1], "r")) == NULL) {
			fprintf(stderr,
				"%s: can't open %s\n", argv[0], argv[argc-1]);
			exit(0);
		}
	} else {in = stdin;}

	/*
	 * Just copy to output until we see an eexec.
	 */
COPYFILE:
	while (1) {
		if (fgets(in_buff, BUFSIZ, in) == NULL) goto fin;
		if (strcmp(in_buff, "currentfile eexec\n") == 0)
			break;
		fprintf(stdout, "%s", in_buff);
	}

	skip_salt = EEXEC_SKIP_BYTES + 1;
	er = ER;
	ec1 = EC1;
	ec2 = EC2;

	for (in_ptr = 999, in_size = 0, o=0, obase=0;;) {
	    switch(state) {
	    case CINZ_STATE:
		cr = CR;
		cc1 = CC1;
		cc2 = CC2;
		csalt = lenIV + 1;
		for (count_pos = &out_buff[o-5];
			(count_pos >= out_buff) && (*count_pos != ' ');
			count_pos--)
				;

		/* This can be at the beginning of a line */
		if (*count_pos == ' ')
			count_pos++;

		count = atoi(count_pos);
				
		/*
		 * Change "count RD" to "{"
		 */
		o = count_pos - out_buff;
		out_buff[o++] = '{';
		out_buff[o] = '\0';

		state = CHAR_STATE;
		
	    case CHAR_STATE:
		for ( ; count > 0; i += 2) {
			/*
			 * Translate the buffer.
			 */
			byte = CDeCrypt();
			if (byte == 11) {			/* return */
				o += sprintf(&out_buff[o], " %s",
					commands[byte].command);
				out_check();
				break;
			} else if (byte == 12) {	/* escape */
				byte = CDeCrypt();
				if (byte > MAX_ESCAPE) {
					o += sprintf(&out_buff[o],
						" not_defined_e%d", byte);
					out_check();
				} else {
					o += sprintf(&out_buff[o], " %s",
						escapes[byte].command);
					out_check();
				}
				continue;
			} else if (byte < 32) {
				o += sprintf(&out_buff[o], " %s",
					commands[byte].command);
				out_check();
			}
			if (byte >= 32) {
				if (byte <= 246) {
					o += sprintf(&out_buff[o], " %d",
						byte  - 139);
					out_check();
				} else if ((byte >= 247) && (byte <= 250)) {
					o += sprintf(&out_buff[o], " %d",
						(byte  - 247) * 256
						+ CDeCrypt() + 108);
					out_check();
				}
				else if ((byte >= 251) && (byte <= 254)) {
					o += sprintf(&out_buff[o], " %d",
						-((int)byte  - 251) * 256
						- (int)CDeCrypt() - 108);
					out_check();
				}
				else if (byte == 255) {
					value = CDeCrypt();
					value <<= 8;
					value += CDeCrypt();
					value <<= 8;
					value += CDeCrypt();
					value <<= 8;
					value += CDeCrypt();
					o +=sprintf(&out_buff[o],
	/* NOTE: long value */					" %ld", value);
					out_check();
				}
			}
		}

		o += sprintf(&out_buff[o], " }");
		out_check();
		state = DATA_STATE;
		
	case DATA_STATE:
		/*
		 * Decrypt a line of hex.
		 */
		    plainchar = EDeCrypt();

		    out_buff[o++] = plainchar;
		    out_check();

		    if ((o >=4) && !strncmp(" RD ", &out_buff[o-4], 4)) {
			state = CINZ_STATE;
			break;
		    }
		    if ((o >=4) && !strncmp(" -| ", &out_buff[o-4], 4)) {
			state = CINZ_STATE;
			break;
		    }
		    if (plainchar == '\n') {
			if (argc==3 && strstr(out_buff,"definefont")) goto fin;
			if (strncmp("/lenIV", out_buff, 6) == 0)
				lenIV = atoi(out_buff + 7);
			if ((count_pos =
				strstr(out_buff,"currentfile closefile"))
				!= NULL){
				strcpy(count_pos, "\n");
				fputs(out_buff, stdout);
				goto COPYFILE;
			}
			fputs(out_buff, stdout);
			o = 0;
			obase = 0;
		    }
	    } /* switch */
	} /* for (reading file) */

fin:	fclose(in);

	exit(0);
}

-- 
Kevin O'Gorman ( kevin at kosman.UUCP, kevin%kosman.uucp at nrc.com )
voice: 805-984-8042 Vital Computer Systems, 5115 Beachcomber, Oxnard, CA  93035
Non-Disclaimer: my boss is me, and he stands behind everything I say.



More information about the Alt.sources mailing list