#ifndef USEPXL
/* modified by Peter Damron 1987 University of Washington */
/*---------------------------------------------------------------------*/
#ifndef lint
static char rcsid[] = "$Header: gf.c,v 2.0 88/06/07 15:02:50 peterd Rel2 $";
#endif lint
/*---------------------------------------------------------------------*/

#include <stdio.h>
#include "gf.h"

/*---------------------------------------------------------------------*/
/* This file contains routines for reading character descriptions from
   GF files (the output of Metafont).  The following functions are defined:
   gf_gettochar() finds the next character, returns its code (reduced modulo
		256 if necessary), and sets global variables gf_min_m, gf_max_m,
		gf_min_n, gf_max_n.  A result of -1 indicates that there are no
		more characters and the postamble has been found.
   gf_readbits() After a character is found, this routine fills array gf_bits[]
		with the character image.  The image is represented in
		Postscript format: the bits are packed into bytes
		(most significant bit first), and the bytes scan the image
		left-to-right, bottom-to-top.
		Set globals gf_num_cols, gf_num_rows, gf_x_offset, gf_y_offset
		(the latter give the offset of the origin from the upper
		bottom corner of the image, up being a positive gf_y_offset).
   gf_readpost() After the postamble is found, this routine reads it and
		sets the remaining global variables declared below.
   gf_seekpost() rapidly finds the postamble by random accessing the file.
   gf_seekchar(c) uses fseek() to find a character with code c (modulo 256),
		returning a nonzero result if successful.  Global variables
		are set as they are by gf_gettochar.  GF files may contain
		many characters with the same code, in which case subsequent
		calls to gf_seekchar(c) with the same c will find the other
		versions.  This routine should only be called after gf_readpost().
   No random access is attempted if gf_seekpost() and gf_seekchar() are not used.
*/
/*---------------------------------------------------------------------*/

/* use macros versus functions */
#define MACROS 1

#ifndef TRUE
#define FALSE	0
#define TRUE	1
#endif TRUE

/*---------------------------------------------------------------------*/

int gf_min_m;		/* min horz bounding box for character */
int gf_max_m;		/* max horz bounding box for character */
int gf_min_n;		/* min vert bounding box for character */
int gf_max_n;		/* max vert bounding box for character */
int32 gf_charfam;	/* high order bytes of character code */
int gf_cur_char;	/* current character - for error messages */

/*---------------------------------------------------------------------*/
/* these variables initialized by gf_readpost() */

int gf_font_min_m;	/* min horz overall bounds of this font */
int gf_font_max_m;	/* max horz overall bounds of this font */
int gf_font_min_n;	/* min vert overall bounds of this font */
int gf_font_max_n;	/* max vert overall bounds of this font */
int32 designsize;	/* font size in points scaled by 1<<20 */
int32 gf_checksum;	/* should match TFM file and DVI file */
int32 gf_hppp;		/* horizontal and vertical pixels/point scaled 1<<16 */
int32 gf_vppp;		/* horizontal and vertical pixels/point scaled 1<<16 */

int gf_char_wd[MAXCHARS];	/* character width in pixels, rounded if necessary */
int32 gf_tfm_wd[MAXCHARS];	/* width as a fraction of designsize scaled 1<<20 */
char gf_char_exists[MAXCHARS];	/* nonzero indicates presence of a char_loc */
int gf_bc;			/* first nonzero gf_char_exists entries */
int gf_ec;			/* last nonzero gf_char_exists entries */

int32 gf_char_pointer[MAXCHARS];	/* used by gf_seekchar() */
int32 gf_backpointer;		/* set by gf_gettochar() for use by gf_seekchar() */

/*---------------------------------------------------------------------*/

#ifndef MACROS
void gf_beginc();	/* prepare to paint white at (gf_min_m,gf_max_n) */
void gf_paint();	/* paint d pixels and switch colors */
void gf_skip();		/* finish a row and skip d rows */
void gf_endc();		/* finish the last row */
#endif not MACROS

/*---------------------------------------------------------------------*/

extern void bad_gf();	/* aborts the program if the gf file is invalid */ 
FILE *gf_infile = stdin;	/* change this externally if necessary */
char *gf_filename = 0;		/* name of the currently open font file */

/*---------------------------------------------------------------------*/
/* The following macros describe gf file format */

#define GF_paint_0	0
#define GF_last_paint	63
#define GF_paint1	64
#define GF_paint2	65
#define GF_paint3	66
#define GF_boc		67
#define GF_boc1		68
#define GF_eoc		69
#define GF_skip0	70
#define GF_skip1	71
#define GF_skip2	72
#define GF_skip3	73
#define GF_new_row_0	74
#define GF_last_new_row	238
#define GF_xxx1		239
#define GF_xxx2		240
#define GF_xxx3		241
#define GF_xxx4		242
#define GF_yyy		243
#define GF_no_op	244
#define GF_char_loc	245
#define GF_char_loc0	246
#define GF_pre		247
#define GF_post		248
#define GF_postpost	249
#define undefined_cases	250: case 251: case 252: case 253: case 254: case 255
#define GF_trailer	223
#define GF_version	131

/*---------------------------------------------------------------------*/
/* misc macros for accessing bitmap */

#define row_start(n) (&(gf_bits[(n - gf_min_n)*gf_rowbytes]))
#define m_byte(m) ((m - gf_min_m)>>3)	/* byte within a row containing bit m */
#define m_bit(m) (7-(m - gf_min_m)&0x7)	/* bit within byte for a given m */
					/* bit m on byte boundary? */
#define m_notbyteboundary(m) ((m - gf_min_m)&0x7)

/*---------------------------------------------------------------------*/

#define BITBUFSIZE 20000

unsigned char gf_bits[BITBUFSIZE];
int gf_num_cols;			/* num cols (bits) in bitmap */
int gf_num_rows;			/* num rows (bits) in bitmap */
int gf_num_bytes;			/* num bytes in bitmap */
int gf_x_offset;			/* horz offset of bitmap */
int gf_y_offset;			/* vert offset of bitmap */

int gf_rowbytes;			/* num bytes/row in bitmap */
static int gf_m0;		/* current horz bit number (column) */

#ifndef MACROS
static int gf_m;		/* current horz bit number (column) */
static int gf_n;		/* current horz bit number (row) */
static int gf_paint_switch;	/* paint white or black */
static unsigned char *gf_row_ptr;	/* pointer to start of current row */
#endif not MACROS

/*---------------------------------------------------------------------*/
/* All gf file input goes through the following routines */

#define getbyte()	(getc(gf_infile)&0xff)

#ifdef NOTDEF
int getbyte()
{
	int b;
	b = getc(gf_infile) & 0xff;
	printf("Getting %d\n",b);
	return b;
}
#endif NOTDEF

#define skip1byte()	getc(gf_infile)

int get2bytes()
{	register int ans;
	ans = getbyte() << 8;
	ans += getbyte();
	return ans;
}

int32 get3bytes()
{	register int32 ans;
	ans = getbyte() << 16;
	ans += getbyte() << 8;
	ans += getbyte();
	return ans;
}

int32 get4bytes()
{
	register int32 ans;
	ans = getbyte() << 24;
	ans += getbyte() << 16;
	ans += getbyte() << 8;
	ans += getbyte();
	return ans;
}

#ifdef MACROS

#define skip_bytes(n)  { \
		while (n-- > 0) skip1byte(); \
	}

#else not MACROS

void skip_bytes(n)
	int n;
{
	while (n-- > 0) skip1byte();
}
#endif MACROS

/*---------------------------------------------------------------------*/
/* In the unlikely event of a character code outside of the range 0..255,
   the high order bytes are placed in the global variable gf_charfam.
   If no boc command is encountered, the result is -1 and the last byte read
   is guaranteed to be the post command.
*/

int gf_gettochar()
{
	int32 c;		/* the character code to be returned */
	register int x;		/* temporary */
	register int len;	/* temporary */

	for(;;) switch(getbyte())
	{
	case GF_yyy: skip1byte();		/* intended to fall through 3 times */
	case GF_paint3: case GF_skip3: skip1byte();
	case GF_paint2: case GF_skip2: skip1byte();
	case GF_paint1: case GF_skip1: skip1byte(); continue;

	case GF_boc: c = get4bytes();
		gf_backpointer = get4bytes();
		gf_min_m = get4bytes();
		gf_max_m = get4bytes();
		gf_min_n = get4bytes();
		gf_max_n = get4bytes();
		gf_charfam = c<0 ? -((-c)>>8) : c>>8;
		gf_cur_char = c&0377;
		return c&0377;

	case GF_boc1: c = getbyte();
		gf_backpointer =  -1;
		x = getbyte();		/* del_m */
		gf_max_m = getbyte();
		gf_min_m = gf_max_m - x;
		x = getbyte();		/* del_n */
		gf_max_n = getbyte();
		gf_min_n = gf_max_n - x;
		gf_cur_char = c;
		return c;

	case GF_pre: if (getbyte() != GF_version) bad_gf(1);
		len = getbyte();
		skip_bytes(len);
		continue;

	case GF_xxx1:
		len = getbyte();
		skip_bytes(len);
		continue;
	case GF_xxx2:
		len = get2bytes();
		skip_bytes(len);
		continue;
	case GF_xxx3:
		len = get3bytes();
		skip_bytes(len);
		continue;
	case GF_xxx4:
		len = get4bytes();
		skip_bytes(len);
		continue;

	case GF_post: return -1;

	case GF_char_loc: case GF_char_loc0: case GF_postpost: case undefined_cases:
		bad_gf(2);

	default: /* do nothing */;
	}
}

/*---------------------------------------------------------------------*/
/* read the post-amble of a gf format font file */

void gf_readpost()
{
	register int i;
	register int b;
	register int c;

	get4bytes();	/* ignore back pointer to font-wide xxx commands */
	designsize = get4bytes();
	gf_checksum = get4bytes();
	gf_hppp = get4bytes();
	gf_vppp = get4bytes();
	gf_font_min_m = get4bytes();
	gf_font_max_m = get4bytes();
	gf_font_min_n = get4bytes();
	gf_font_max_n = get4bytes();

	for(i = 0; i < MAXCHARS; i++) {
		gf_char_exists[i] = FALSE;
		gf_char_wd[i] = 0;
		gf_tfm_wd[i] = 0;
		gf_char_pointer[i] =  -1;
	}
	gf_bc = MAXCHARS;
	gf_ec = 0;
	for (;;) {
		b = getbyte();
		c = getbyte();
		if (b == GF_char_loc0) {
			gf_char_wd[c] = getbyte();
		} else if (b == GF_char_loc) {
			gf_char_wd[c] = (get4bytes()+0100000)>>16;
			get4bytes();		/* skip dy */
		} else {
			break;
		}
		gf_tfm_wd[c] = get4bytes();
		gf_char_pointer[c] = get4bytes();
		gf_char_exists[c] = TRUE;
		if (gf_bc>c) gf_bc = c;
		if (gf_ec<c) gf_ec = c;
	}
	gf_cur_char = -1;
}

/*---------------------------------------------------------------------*/
/* find the post-amble of a gf format font file */

void gf_seekpost()
{
	int c;

	fseek(gf_infile, -5L, 2);			/* skip four 223's */
	do {
		c = getbyte();
		fseek(gf_infile, -2L, 1);
	} while (c == GF_trailer);
	if (c != GF_version) bad_gf(5);		/* check version number */
	fseek(gf_infile, -3L, 1);		/* back up to the pointer */
	if (fseek(gf_infile, (long) get4bytes(), 0) < 0) bad_gf(6);
	if (getbyte() != GF_post) bad_gf(7);
	gf_cur_char = -1;
}

/*---------------------------------------------------------------------*/
/* find a character definition in a gf format font file */

int gf_seekchar(c)
	int c;
{
	if (c < 0 || c > MAXCHARS-1 || gf_char_pointer[c] < 0) return FALSE;
	if (fseek(gf_infile, gf_char_pointer[c], 0) < 0) bad_gf(8);
	if (gf_gettochar() != c) bad_gf(9);
	gf_cur_char = c;
	return TRUE;
}

/*---------------------------------------------------------------------*/
/* begin a character, initialize data */

#ifdef MACROS

#define gf_beginc(dummy)  { \
		gf_num_cols = gf_max_m - gf_min_m + 1; \
		gf_num_rows = gf_max_n - gf_min_n + 1; \
		gf_x_offset = -gf_min_m; \
		gf_y_offset = -gf_min_n; \
		gf_rowbytes = (gf_num_cols+7)/8; \
		gf_num_bytes = gf_num_rows*gf_rowbytes; \
		if (gf_num_bytes >= BITBUFSIZE) \
			bad_gf(10); /* really, should allocate a bigger one */ \
		gf_m = gf_m0 = gf_min_m; \
		gf_n = gf_max_n; \
		gf_row_ptr = row_start(gf_max_n); \
		bzero(gf_bits, gf_num_bytes); \
		gf_paint_switch = FALSE; \
	}

#else not MACROS

void gf_beginc()
{
	gf_num_cols = gf_max_m - gf_min_m + 1;
	gf_num_rows = gf_max_n - gf_min_n + 1;
	gf_x_offset = -gf_min_m;
	gf_y_offset = -gf_min_n;
	gf_rowbytes = (gf_num_cols+7)/8;
	gf_num_bytes = gf_num_rows*gf_rowbytes;
	if (gf_num_bytes >= BITBUFSIZE) {
		bad_gf(10); /* really, should allocate a bigger one */
	}
	gf_m = gf_m0 = gf_min_m;
	gf_n = gf_max_n;
	gf_row_ptr = row_start(gf_max_n);
	bzero(gf_bits, gf_num_bytes);
	gf_paint_switch = FALSE;
}
#endif MACROS

/*---------------------------------------------------------------------*/
/* paint a portion of a row of bits into the bitmap */
/* this is a decoding of a run length encoding of a bitmap */
/* d is the number of bits (run length) to encode */
/* gf_paint_switch is a true for one-bits and false for zero bits */
/* this is the old version */

#ifdef NOTDEF
void gf_paint(d)
	register int d;
{
	if (d>0) {
		if (gf_paint_switch) {
			while (d-- >0) {
				gf_row_ptr[m_byte(gf_m)] |= 1<<m_bit(gf_m);
				gf_m++;
			}
		} else {
			gf_m += d;
		}
	}
	gf_paint_switch ^= 1;
}
#endif NOTDEF

/*---------------------------------------------------------------------*/
/* paint a portion of a row of bits into the bitmap */
/* this is a decoding of a run length encoding of a bitmap */
/* d is the number of bits (run length) to encode */
/* gf_paint_switch is a true for one-bits and false for zero bits */
/* row-ptr is a pointer to the beginning of this row */
/* gf_m is the distance (bits) from the beginning of the row (output bitmap) */

#ifdef MACROS

#define gf_paint(len)  { \
	if (len > 0) { \
	    if (gf_paint_switch) { \
		/* paint white (==1) */ \
		/* first fill in the first partial byte */ \
		while ((len > 0) && m_notbyteboundary(gf_m)) { \
		    gf_row_ptr[m_byte(gf_m)] |= 1 << m_bit(gf_m); \
		    gf_m += 1; \
		    len -= 1; \
		} \
		/* now fill in the whole bytes */ \
		while (len > 0) { \
			/* note (gf_m & 0x7 == 0) */ \
			/* this is an even byte boundary */ \
			/* so we know what to put into this byte */ \
		    if (len >= 8) { \
			/* this byte is full */ \
			gf_row_ptr[m_byte(gf_m)] = 0xFF; \
			gf_m += 8; \
			len -= 8; \
		    } else { \
			/* this byte is partial */ \
			/* len&7 == len div 8 */ \
			gf_row_ptr[m_byte(gf_m)] = (0xFF << (8-len)) & 0xFF; \
			gf_m += len; \
			len = 0; \
		    } \
		} \
		gf_paint_switch = FALSE; \
	    } else { \
		/* paint black (==0) is easy */ \
		gf_m += len; \
		gf_paint_switch = TRUE; \
	    } \
	} else { \
	    /* len <= 0 */ \
	    if (len < 0) { \
		Warning("internal - unexpected value in gf_paint %d\n",len); \
	    } \
	    gf_paint_switch = !gf_paint_switch; \
	} \
    }

#else not MACROS

void gf_paint(len)
	register int len;
{
    /* why was I called? */
    if (len <= 0) {
	if (len < 0) {
	    Warning("internal - unexpected value in gf_paint %d\n",len);
	}
	gf_paint_switch = !gf_paint_switch;
	return;
    }

    /* paint black (==0) is easy */
    if (!gf_paint_switch) {
	gf_m += len;
	gf_paint_switch = TRUE;
	return;
    }

    /* paint white (==1) */
    /* first fill in the first partial byte */
    while ((len > 0) && m_notbyteboundary(gf_m)) {
	gf_row_ptr[m_byte(gf_m)] |= 1 << m_bit(gf_m);
	gf_m += 1;
	len -= 1;
    }

    /* now fill in the whole bytes */
    while (len > 0) {
	    /* note (gf_m & 0x7 == 0) */
	    /* this is an even byte boundary */
	    /* so we know what to put into this byte */
	if (len >= 8) {
	    /* this byte is full */
	    gf_row_ptr[m_byte(gf_m)] = 0xFF;
	    gf_m += 8;
	    len -= 8;
	} else {
	    /* this byte is partial */
	    /* len&7 == len div 8 */
	    gf_row_ptr[m_byte(gf_m)] = (0xFF << (8-len)) & 0xFF;
	    gf_m += len;
	    len = 0;
	}
    }
    gf_paint_switch = FALSE;
}
#endif MACROS

/*---------------------------------------------------------------------*/
/* skip to the next row of bits */

#ifdef MACROS

#define gf_skip(d)  { \
		gf_n -= d+1; \
		gf_m = gf_m0; \
		gf_row_ptr = row_start(gf_n); \
		gf_paint_switch = FALSE; \
	}

#else not MACROS

void gf_skip(d)
	int d;
{
	gf_n -= d+1;
	gf_m = gf_m0;
	gf_row_ptr = row_start(gf_n);
	gf_paint_switch = FALSE;
}
#endif MACROS

/*---------------------------------------------------------------------*/
/* end of character */

#ifdef MACROS

#define gf_endc(dummy)

#else not MACROS

void gf_endc() { /* do nothing */ }

#endif MACROS

/*---------------------------------------------------------------------*/
/* gf_readbits reads a raster description from the gf file and uses the external
   routines to actually process the raster information.
*/

void gf_readbits()
{
	/* register unsigned char byt; */
	register int byt;
	register int len;
#ifdef MACROS
	register unsigned char *gf_row_ptr;	/* start of current row */
	register int gf_m;		/* current horz bit number (column) */
	register int gf_paint_switch;	/* paint white or black */
	register int gf_n;		/* current horz bit number (row) */
#endif MACROS

	gf_beginc();
	for (;;) {
		byt = getbyte();
		if (byt <= GF_last_paint) {
		    len = byt - GF_paint_0;
		    gf_paint(len);
		}
		if (byt < GF_new_row_0) {
			switch(byt) {
			case GF_paint1:
				len = getbyte();
				gf_paint(len);
				continue;
			case GF_paint2:
				len = get2bytes();
				gf_paint(len);
				continue;
			case GF_paint3:
				len = get3bytes();
				gf_paint(len);
				continue;
			case GF_boc:
			case GF_boc1:
				bad_gf(3);
			case GF_eoc:
				goto finish;
			case GF_skip0:
				gf_skip(0);
				continue;
			case GF_skip1:
				len = getbyte();
				gf_skip(len);
				continue;
			case GF_skip2:
				len = get2bytes();
				gf_skip(len);
				continue;
			case GF_skip3:
				len = get3bytes();
				gf_skip(len);
				continue;
			}
		} else if (byt <= GF_last_new_row) {
			gf_skip(0);
			len = byt - GF_new_row_0;
			gf_paint(len);
		} else {
			switch(byt) {
			case GF_xxx1:
				len = getbyte();
				skip_bytes(len);
				continue;
			case GF_xxx2:
				len = get2bytes();
				skip_bytes(len);
				continue;
			case GF_xxx3:
				len = get3bytes();
				skip_bytes(len);
				continue;
			case GF_xxx4:
				len = get4bytes();
				skip_bytes(len);
				continue;
			case GF_yyy:
				get4bytes();
				continue;
			case GF_no_op:
				continue;
			default:
				bad_gf(4);
			}
		}
	}
finish:	gf_endc();
}

/*---------------------------------------------------------------------*/
/* report errors */

void bad_gf(n)
	int n;
{
	Fatal("bad gf font file %s\n  char %d offset %d case %d\n",
		gf_filename,gf_cur_char,ftell(gf_infile),n);
}

/*---------------------------------------------------------------------*/
#endif USEPXL

