/*
 * Copyright (c) 1994 Paul Vojta.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "config.h"

/***
 ***	GF font reading routines.
 ***	Public routines are read_GF_index and read_GF_char.
 ***/

#define	PAINT_0		0
#define	PAINT1		64
#define	PAINT2		65
#define	PAINT3		66
#define	BOC		67
#define	BOC1		68
#define	EOC		69
#define	SKIP0		70
#define	SKIP1		71
#define	SKIP2		72
#define	SKIP3		73
#define	NEW_ROW_0	74
#define	NEW_ROW_MAX	238
#define	XXX1		239
#define	XXX2		240
#define	XXX3		241
#define	XXX4		242
#define	YYY		243
#define	NO_OP		244
#define	CHAR_LOC	245
#define	CHAR_LOC0	246
#define	PRE		247
#define	POST		248
#define	POST_POST	249

#define	GF_ID_BYTE	131
#define	TRAILER		223		/* Trailing bytes at end of file */

static	FILE	*GF_file;

static	void
expect(ch)
	ubyte ch;
{
	ubyte ch1 = one(GF_file);

	if (ch1 != ch)
		oops("Bad GF file:  %d expected, %d received.", ch, ch1);
}

static	void
too_many_bits(ch)
	ubyte ch;
{
	oops("Too many bits found when loading character %d", ch);
}

/*
 *	Public routines
 */


static	void
#if	NeedFunctionPrototypes
read_GF_char(register struct font *fontp, wide_ubyte ch)
#else	/* !NeedFunctionPrototypes */
read_GF_char(fontp, ch)
	register struct font *fontp;
	ubyte	ch;
#endif	/* NeedFunctionPrototypes */
{
	register struct glyph *g;
	ubyte	cmnd;
	int	min_m, max_m, min_n, max_n;
	BMUNIT	*cp, *basep, *maxp;
	int	bytes_wide;
	Boolean	paint_switch;
#define	White	False
#define	Black	True
	Boolean	new_row;
	int	count;
	int	word_weight;

	g = &fontp->glyph[ch];
	GF_file = fontp->file;

	if(debug & DBG_PK)
	    Printf("Loading gf char %d", ch);

	for (;;) {
	    switch (cmnd = one(GF_file)) {
		case XXX1:
		case XXX2:
		case XXX3:
		case XXX4:
		    Fseek(GF_file, (long) num(GF_file,
			WIDENINT cmnd - XXX1 + 1), 1);
		    continue;
		case YYY:
		    (void) four(GF_file);
		    continue;
		case BOC:
		    (void) four(GF_file);	/* skip character code */
		    (void) four(GF_file);	/* skip pointer to prev char */
		    min_m = sfour(GF_file);
		    max_m = sfour(GF_file);
		    g->x = -min_m;
		    min_n = sfour(GF_file);
		    g->y = max_n = sfour(GF_file);
		    g->bitmap.w = max_m - min_m + 1;
		    g->bitmap.h = max_n - min_n + 1;
		    break;
		case BOC1:
		    (void) one(GF_file);	/* skip character code */
		    g->bitmap.w = one(GF_file);	/* max_m - min_m */
		    g->x = g->bitmap.w - one(GF_file);	/* ditto - max_m */
		    ++g->bitmap.w;
		    g->bitmap.h = one(GF_file) + 1;
		    g->y = one(GF_file);
		    break;
		default:
		    oops("Bad BOC code:  %d", cmnd);
	    }
	    break;
	}
	paint_switch = White;

	if (debug & DBG_PK)
	    Printf(", size=%dx%d, dvi_adv=%ld\n", g->bitmap.w, g->bitmap.h,
		g->dvi_adv);

	alloc_bitmap(&g->bitmap);
	cp = basep = (BMUNIT *) g->bitmap.bits;
/*
 *	Read character data into *basep
 */
	bytes_wide = ROUNDUP((int) g->bitmap.w, BITS_PER_BMUNIT)
	    * BYTES_PER_BMUNIT;
	maxp = ADD(basep, g->bitmap.h * bytes_wide);
	bzero(g->bitmap.bits, g->bitmap.h * bytes_wide);
	new_row = False;
	word_weight = BITS_PER_BMUNIT;
	for (;;) {
	    count = -1;
	    cmnd = one(GF_file);
	    if (cmnd < 64) count = cmnd;
	    else if (cmnd >= NEW_ROW_0 && cmnd <= NEW_ROW_MAX) {
		count = cmnd - NEW_ROW_0;
		paint_switch = White;	/* it'll be complemented later */
		new_row = True;
	    }
	    else switch (cmnd) {
		case PAINT1:
		case PAINT2:
		case PAINT3:
		    count = num(GF_file, WIDENINT cmnd - PAINT1 + 1);
		    break;
		case EOC:
		    if (cp >= ADD(basep, bytes_wide)) too_many_bits(ch);
		    return;
		case SKIP1:
		case SKIP2:
		case SKIP3:
		    *((char **) &basep) +=
			num(GF_file, WIDENINT cmnd - SKIP0) * bytes_wide;
		case SKIP0:
		    new_row = True;
		    paint_switch = White;
		    break;
		case XXX1:
		case XXX2:
		case XXX3:
		case XXX4:
		    Fseek(GF_file, (long) num(GF_file,
			WIDENINT cmnd - XXX1 + 1), 1);
		    break;
		case YYY:
		    (void) four(GF_file);
		    break;
		case NO_OP:
		    break;
		default:
		    oops("Bad command in GF file:  %d", cmnd);
	    } /* end switch */
	    if (new_row) {
		*((char **) &basep) += bytes_wide;
		if (basep >= maxp || cp >= basep) too_many_bits(ch);
		cp = basep;
		word_weight = BITS_PER_BMUNIT;
		new_row = False;
	    }
	    if (count >= 0) {
		while (count)
		    if (count <= word_weight) {
#ifndef	MSBITFIRST
			if (paint_switch)
			    *cp |= bit_masks[count] <<
				(BITS_PER_BMUNIT - word_weight);
#endif
			word_weight -= count;
#ifdef	MSBITFIRST
			if (paint_switch)
			    *cp |= bit_masks[count] << word_weight;
#endif
			break;
		    }
		    else {
			if (paint_switch)
#ifndef	MSBITFIRST
			    *cp |= bit_masks[word_weight] <<
				(BITS_PER_BMUNIT - word_weight);
#else
			    *cp |= bit_masks[word_weight];
#endif
			cp++;
			count -= word_weight;
			word_weight = BITS_PER_BMUNIT;
		    }
		paint_switch = 1 - paint_switch;
	    }
	} /* end for */
}


void
read_GF_index(fontp, hushcs)
	register struct font	*fontp;
	wide_bool		hushcs;
{
	int	hppp, vppp;
	ubyte	ch, cmnd;
	register struct glyph *g;
	long	checksum;

	fontp->read_char = read_GF_char;
	GF_file = fontp->file;
	if (debug & DBG_PK)
	    Printf("Reading GF pixel file %s\n", fontp->filename);
/*
 *	Find postamble.
 */
	Fseek(GF_file, (long) -4, 2);
	while (four(GF_file) != ((unsigned long) TRAILER << 24 | TRAILER << 16
		| TRAILER << 8 | TRAILER))
	    Fseek(GF_file, (long) -5, 1);
	Fseek(GF_file, (long) -5, 1);
	for (;;) {
	    ch = one(GF_file);
	    if (ch != TRAILER) break;
	    Fseek(GF_file, (long) -2, 1);
	}
	if (ch != GF_ID_BYTE) oops("Bad end of font file %s", fontp->fontname);
	Fseek(GF_file, (long) -6, 1);
	expect(POST_POST);
	Fseek(GF_file, sfour(GF_file), 0);	/* move to postamble */
/*
 *	Read postamble.
 */
	expect(POST);
	(void) four(GF_file);		/* pointer to last eoc + 1 */
	(void) four(GF_file);		/* skip design size */
	checksum = four(GF_file);
	if (!hushcs && checksum && fontp->checksum
            && checksum != fontp->checksum)
	    Fprintf(stderr,
		"Checksum mismatch (dvi = %lu, gf = %lu) in font file %s\n",
		fontp->checksum, checksum, fontp->filename);
	hppp = sfour(GF_file);
	vppp = sfour(GF_file);
	if (hppp != vppp && (debug & DBG_PK))
	    Printf("Font has non-square aspect ratio %d:%d\n", vppp, hppp);
	(void) four(GF_file);		/* skip min_m */
	(void) four(GF_file);		/* skip max_m */
	(void) four(GF_file);		/* skip min_n */
	(void) four(GF_file);		/* skip max_n */
/*
 *	Prepare glyph array.
 */
	fontp->glyph = (struct glyph *) xmalloc(256 * sizeof(struct glyph),
	    "glyph array");
	bzero((char *) fontp->glyph, 256 * sizeof(struct glyph));
/*
 *	Read glyph directory.
 */
	while ((cmnd = one(GF_file)) != POST_POST) {
	    int addr;

	    ch = one(GF_file);			/* character code */
	    g = &fontp->glyph[ch];
	    switch (cmnd) {
		case CHAR_LOC:
		    /* g->pxl_adv = sfour(GF_file); */
		    (void) four(GF_file);
		    (void) four(GF_file);	/* skip dy */
		    break;
		case CHAR_LOC0:
		    /* g->pxl_adv = one(GF_file) << 16; */
		    (void) one(GF_file);
		    break;
		default:
		    oops("Non-char_loc command found in GF preamble:  %d",
			cmnd);
	    }
	    g->dvi_adv = fontp->dimconv * sfour(GF_file);
	    addr = four(GF_file);
	    if (addr != -1) g->addr = addr;
	    if (debug & DBG_PK)
		Printf("Read GF glyph for character %d; dy = %ld, addr = %x\n",
			ch, g->dvi_adv, addr);
	}
}
