#ifdef USEGF

#ifndef lint
static char sccsid[] = "@(#)gf.c	3.1\t11/16/89";
#endif lint

/* This file contains routines for reading character descriptions from GF
 * files (the output of Metafont).  The following functions are defined: 
 *
 * gettochar() finds the next character, returns its code (reduced modulo
 * 256 if necessary), and sets global variables min_m, max_m, min_n, max_n.
 * A result of -1 indicates that there are no more characters and the postamble
 * has been found.
 *
 * readbits()	After a character is found, this routine fills array 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 num_cols,
 * num_rows, x_offset, y_offset (the latter give the offset of the origin from
 * the upper bottom corner of the image, up being a positive y_offset).
 *
 * readpost()	After the postamble is found, this routine reads it and sets
 * the remaining global variables declared below.
 *
 * seekpost()	rapidly finds the postamble by random accessing the file.
 *
 * 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 gettochar.  GF files may contain many characters with the 
 * same code, in which case subsequent calls to seekchar(c) with the same
 * c will find the other versions.  This routine should only be called after
 * readpost(). No random access is attempted if seekpost() and seekchar() 
 * are not used.
 */

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

extern void Warning(), Fatal();

void  gf_beginc();	/* prepare to paint white at (min_m,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 */
void  bad_gf();		/* aborts the program if the gf file is invalid */

extern FILE *outfp;
extern char *digit;
extern int hconv;
extern font_entry *fontptr;
#ifdef STATS
extern bool C_stats;
extern int Snbpxl, Sonbpx, Sndc;
#endif STATS

FILE *gfin;
char_entry *gfcp;
int   min_m, max_m, min_n, max_n;	/* bounding box for character */
long  charfam;		/* high order bytes of character code */

int   font_min_m, font_max_m, font_min_n, font_max_n;	/* overall bounds */
int   char_wd[256];	/* character width in pixels, rounded if necessary */
long  tfm_wd[256];	/* width as a fraction of designsize scaled 1<<20 */
char  char_exists[256];	/* nonzero entries indicate presence of a char_loc */
int   gf_bc, gf_ec;	/* first and last nonzero char_exists entries */

long  char_pointer[256];/* used by seekchar() */
long  backpointer;	/* set by gettochar() for use by seekchar() */


void 
bad_gf(n)
int   n;
{
    Fatal("Bad gf file, case %d\n", n);
}


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

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

#define skip1byte()	getc(gfin)

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

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

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

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



/* In the unlikely event of a character code outside of the range 0..255, the
 * high order bytes are placed in the global variable charfam. If no boc
 * command is encountered, the result is -1 and the last byte read is
 * guaranteed to be the post command. */
int 
gettochar()
{
    long  c;		/* the character code to be returned */
    register int x;	/* temporary */

    for (;;)
	switch (getbyte()) {
	case yyy:
	    skip1byte();	/* fall through 3 times */
	case paint3:
	case skip3:
	    skip1byte();
	case paint2:
	case skip2:
	    skip1byte();
	case paint1:
	case skip1:
	    skip1byte();
	    continue;

	case boc:
	    c = get4bytes();
	    backpointer = get4bytes();
	    min_m = get4bytes();
	    max_m = get4bytes();
	    min_n = get4bytes();
	    max_n = get4bytes();
	    charfam = c < 0 ? -((-c) >> 8) : c >> 8;
	    return c & 0377;

	case boc1:
	    c = getbyte();
	    backpointer = -1;
	    x = getbyte();	/* del_m */
	    max_m = getbyte();
	    min_m = max_m - x;
	    x = getbyte();	/* del_n */
	    max_n = getbyte();
	    min_n = max_n - x;
	    return c;

	case pre:
	    if (getbyte() != GF_ID) bad_gf(1);
	    skip_bytes(getbyte());
	    continue;

	case xxx1:
	    skip_bytes(getbyte());
	    continue;
	case xxx2:
	    skip_bytes(get2bytes());
	    continue;
	case xxx3:
	    skip_bytes((int) get3bytes());
	    continue;
	case xxx4:
	    skip_bytes((int) get4bytes());
	    continue;

	case post:
	    return -1;

	case char_loc:
	case char_loc0:
	case postpost:
	case undefined_cases:
	    bad_gf(2);

	default: /* do nothing */ ;
	}
}


/* readbits reads a raster description from the gf file and uses the external
 * routines to actually process the raster information. */
void 
readbits()
{
    register unsigned char b;

    gf_beginc();
    for (;;) {
	b = getbyte();
	if (b <= last_paint)
	    gf_paint((int) (b - paint_0));
	if (b < new_row_0)
	    switch (b) {
	    case paint1:
		gf_paint(getbyte());
		continue;
	    case paint2:
		gf_paint(get2bytes());
		continue;
	    case paint3:
		gf_paint((int) get3bytes());
		continue;
	    case boc:
	    case boc1:
		bad_gf(3);
	    case eoc:
		goto finish;
	    case skip0:
		gf_skip(0);
		continue;
	    case skip1:
		gf_skip(getbyte());
		continue;
	    case skip2:
		gf_skip(get2bytes());
		continue;
	    case skip3:
		gf_skip((int) get3bytes());
		continue;
	    }
	else if (b <= last_new_row) {
	    gf_skip(0);
	    gf_paint((int) (b - new_row_0));
	}
	else
	    switch (b) {
	    case xxx1:
		skip_bytes(getbyte());
		continue;
	    case xxx2:
		skip_bytes(get2bytes());
		continue;
	    case xxx3:
		skip_bytes((int) get3bytes());
		continue;
	    case xxx4:
		skip_bytes((int) get4bytes());
		continue;
	    case yyy:
		(void) get4bytes();
		continue;
	    case no_op:
		continue;
	    default:
		bad_gf(4);
	    }
    }
finish:gf_endc();
}

void  seekpost();	/* forward definition */

int 
Scangf(fep, fileptr)
register font_entry *fep;
FILE *fileptr;
{
    int   i, b, c;
    long  checksum;
    long  hppp, vppp;	/* hor and ver pixels/point scaled 1<<16 */
    register char_entry *cep;
    double cscale;

    gfin = fileptr;
    seekpost();
    (void) get4bytes();	/* ignore back pointer to font-wide xxx commands */
    fep->designsize = get4bytes();
    checksum = get4bytes();
    hppp = get4bytes();
    vppp = get4bytes();
    if (vppp != hppp)
	Warning("font = \"%s\" -- hppp != vppp", fep->name);
    fep->magnification = (int) (hppp * 72.27 / 65536 + 0.5);
    font_min_m = get4bytes();
    font_max_m = get4bytes();
    font_min_n = get4bytes();
    font_max_n = get4bytes();

    cscale = (double) fep->s / (double) (1 << 20);
    for (i = 0; i < 256; i++) {
	char_exists[i] = FALSE;
	char_wd[i] = 0;
	tfm_wd[i] = 0;
	char_pointer[i] = -1;
    }
    gf_bc = 255;
    gf_ec = 0;
    for (;;) {		/* why is all this stuff stored twice? */
	b = getbyte();
	c = getbyte();
	if (b == char_loc0)
	    char_wd[c] = getbyte();
	else if (b == char_loc) {
	    char_wd[c] = (get4bytes() + 0100000) >> 16;
	    (void) get4bytes();	/* skip dy */
	}
	else
	    break;
	tfm_wd[c] = get4bytes();
	char_pointer[c] = get4bytes();
	char_exists[c] = TRUE;
	if (gf_bc > c)
	    gf_bc = c;
	if (gf_ec < c)
	    gf_ec = c;
    }
    for (i = FIRSTFNTCHAR; i <= LASTFNTCHAR; i++) {
	if (char_exists[i]) {
	    cep = &(fep->ch[i]);
	    cep->tfmw = (int) (((double) tfm_wd[i] * cscale) + 0.5);
	    cep->dx = char_wd[i];
	/* cep->dy = ??? */
	    cep->where.isloaded = NOTLOADED;
	    cep->where.address.fileOffset = char_pointer[i];
	}
    }
    return (checksum);
}



void 
seekpost()
{
    int   c;
    (void) fseek(gfin, -5L, 2);	/* skip four 223's */
    do {
	c = getbyte();
	(void) fseek(gfin, -2L, 1);
    } while (c == 223);
    if (c != GF_ID)
	bad_gf(5);	/* check version number */
    (void) fseek(gfin, -3L, 1);	/* back up to the pointer */
    if (fseek(gfin, (long) get4bytes(), 0) < 0)
	bad_gf(6);
    if (getbyte() != post)
	bad_gf(7);
}


int 
seekchar(c)
int   c;
{
    if (c < 0 || c > 255 || char_pointer[c] < 0)
	return FALSE;
    if (fseek(gfin, char_pointer[c], 0) < 0)
	bad_gf(8);
    if (gettochar() != c)
	bad_gf(9);
    char_pointer[c] = backpointer;
    return TRUE;
}

#define BITBUFSIZE 20000
unsigned char bits[BITBUFSIZE];
int   num_cols, num_rows, num_bytes, x_offset, y_offset;
#define row_start(n) (&(bits[(n-min_n)*rowbytes]))
#define m_byte(m) ((m-min_m)>>3)	/* byte within within a row containing
					 * bit m */
#define m_bit(m) (7-(m-min_m)&07)	/* bit within byte for a given m */

static int rowbytes, gf_m0, gf_m, gf_n, paint_switch;
static unsigned char *row_ptr;

void 
gf_beginc()
{
    num_cols = max_m - min_m + 1;
    gfcp->width = num_cols - 1;
    gfcp->height = num_rows = max_n - min_n + 1;
    gfcp->xOffset = x_offset = -min_m;
    gfcp->yOffset = max_n;
    y_offset = -min_n;
    rowbytes = (num_cols + 7) / 8;
    num_bytes = num_rows * rowbytes;
    if (num_bytes >= BITBUFSIZE)
	bad_gf(10);	/* really, should allocate a bigger one */
    gf_m = gf_m0 = min_m;
    gf_n = max_n;
    row_ptr = row_start(max_n);
    bzero((char *) bits, num_bytes);
    paint_switch = 0;
}

void 
gf_paint(d)
register int d;
{
    if (d > 0) {
	if (paint_switch)
	    while (d-- > 0) {
		row_ptr[m_byte(gf_m)] |= 1 << m_bit(gf_m);
		gf_m++;
	    }
	else
	    gf_m += d;
    }
    paint_switch ^= 1;
}

void 
gf_skip(d)
int   d;
{
    gf_n -= d + 1;
    gf_m = gf_m0;
    row_ptr = row_start(gf_n);
    paint_switch = 0;
}

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


void
Loadgfbits(fp, cep)
FILE *fp;
register char_entry *cep;
{
    gfin = fp;
    gfcp = cep;
    (void) gettochar();
    readbits();
    return;
}


/**********************************************************************/
/******************************  EmitGF  ******************************/
/**********************************************************************/
void
EmitGF(c, ce, cmd)
int   c;
char *cmd;
char_entry *ce;
{
    int   nbpl;
    register int i, cc;
    register unsigned char *sl;
    float cw;

    EMITS("[<");
    cc = 2;
    nbpl = (ce->width + 7) >> 3;
    for (i = 0, sl = bits; i < num_bytes; i++, sl++) {
	if (cc > 78) {
	    EMITS("\n  ");
	    cc = 2;
	}
	EMITH(*sl);
	cc += 2;
    }
    cw = (double) ce->tfmw / (double) hconv / (double) fontptr->ps_scale;

#ifdef BUILTIN
	if (fontptr->dic_pack == TRUE) c = ce->pschar;
#endif /* BUILTIN */
    EMIT(outfp, "> %d %d %d %d %d] %d %s\n",
      ce->width, ce->height, ce->xOffset,
      (((int) ce->height) - ce->yOffset) - 1, ce->dx, c, cmd);

#ifdef STATS
    Snbpxl += nbpl * ce->height;
    fontptr->nbpxl += nbpl * ce->height;
    Sonbpx += (ce->width * ce->height + 7) >> 3;
    Sndc += 1;
#endif STATS
}

#endif USEGF
