/*
 * This is the font part of textool, dealing with PK format files.
 * The low level routines to deal with the PK format
 * were transliterated from pktopx
 * See the pktopx documentation for details.
 * For V2.3, 29 Mar 88
-  * Fixed stupid errors in skip_specials() that caused specials to be
missed

 */

#include "defs.h"
#include <stdio.h>
#include <sys/file.h>				/* for access() stuff */
#include <pixrect/pixrect_hs.h>		/* for LoadAChar() */

char	*index();
int	strlen();
char	*strcpy();
char	*sprintf();

/* procedures in sunstuff */
extern void	Fatal();
extern void	Warning();

/* procedures in dvistuff */
extern float	ActualFactor();
extern FILE	*OpenFontFile();

/* procedures here */
void	ReadCharDefs();
void	LoadAChar();

/* a few global variables and data structures */
#include "globals.h"

#define PK_XXX1 240
#define PK_XXX2 241
#define PK_XXX3 242
#define PK_XXX4 243
#define PK_YYY 244
#define PK_POST 245
#define PK_NOP 246
#define PK_PRE 247
#define PK_ID 89

#define ROWSIZE 64		/* maximum width of raster in 16 bit words */

static FILE *pkfp;
static long pk_loc;			/* points to next byte to be read in file */
static long pk_char_start;
static int repeat_count;
static int dyn_f;
static int word_width;
static int char_height;
static int char_width;
static Boolean got_pk_post;
static Boolean turn_on;
static int bit_weight;
static int flag_byte;
static int packet_length;
static int char_code;
static int tfm_width;
static int hor_esc;
static int vert_esc;
static int h_off;
static int v_off;
static int nybindex;
static short row[ROWSIZE];
static short *rowptr;
static short *imageptr;

static int power[16] =
	{1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
	0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000};
static int gpower[17] =
	{0, 1, 3, 7, 15, 0x1f, 0x3f, 0x7f,
	0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff};

#define get_pk_byte() (pk_loc++, getc(pkfp))


int
get_pk_word()
{
	int myword;

	myword = get_pk_byte() << 8;
	return (myword + get_pk_byte());
}


int
get_pk_double()
{
	register int mydouble;

	mydouble = get_pk_byte() << 8;
	mydouble += get_pk_byte();
	mydouble = (mydouble << 8) + get_pk_byte();
	return ((mydouble << 8) + get_pk_byte());
}


			/*
			 * This returns the next nybble from the pk file
			 * The variable nybindex controls which nybble is
			 * returned. This is initialised to 0 in normal_raster()
			 * as a previous normal_raster() may have read an odd
			 * number of nybbles.
			 */
int
get_pk_nybble()
{
	static int mynybble;

	if (nybindex) {
		nybindex = 0;
		return (mynybble & 0xf);
	}
	else {
		mynybble = get_pk_byte();
		nybindex = 1;
		return (mynybble >> 4);
	}
}


			/*
			 * This returns a non-zero value if the next pk file
			 * bit is a one. It is only used in an if (..) so
			 * the actual value is not important.
			 * The variable bit_weight is used to count the bits
			 * between calls to get_pk_byte(). It is initialised
			 * to 0 in bitmap_raster() so a byte is fetched the
			 * first time.
			 * The function is given for illustration. The macro
			 * following is used for speed.
			 */
/*
 *int
 *get_pk_bit()
 *{
 *	static int byte_for_bits;
 *
 *	if (bit_weight <= 1) {
 *		bit_weight = 256;
 *		byte_for_bits = get_pk_byte();
 *	}
 *	bit_weight = bit_weight >> 1;
 *	return (byte_for_bits & bit_weight);
 *}
 */
static int byte_for_bits;
#define get_pk_bit() (((bit_weight<=1) ?\
	(bit_weight = 128, byte_for_bits=get_pk_byte()) : (bit_weight>>=1)),\
	byte_for_bits&bit_weight)


void
send_row()
{
	register short *dp;
	register int i;
	register int repcount;

	dp = imageptr;
	repcount = repeat_count + 1;
	while (repcount--) {
		rowptr = row;
		i = word_width;
		while(i--)
			*dp++ = *rowptr++;
	}
	imageptr=dp;		/* save for next call */
}


void
skip_specials()
{
	register int i;

	do {
		i = 0;
		flag_byte = get_pk_byte();
		if (flag_byte >= PK_XXX1)
			switch (flag_byte) {
				case PK_XXX4:
					i = get_pk_byte();
				case PK_XXX3:
					i = (i<<8) + get_pk_byte();
				case PK_XXX2:
					i = (i<<8) + get_pk_byte();
				case PK_XXX1:
					i = (i<<8) + get_pk_byte();
					while (i--)
						(void)get_pk_byte();
					break;
				case PK_YYY:
					(void)get_pk_double();
					break;
				case PK_POST:
#ifdef DEBUG
					if (ExtraDebug)
						fprintf(stderr, "Got pk_post in skip_specials()\n");
#endif
					got_pk_post = TRUE;
					return;
				case PK_NOP:
					break;
				default:
					Fatal("Bad pk command byte (%d) in skip_specials()",
						flag_byte);
					return;
			}
	} while (flag_byte >= PK_XXX1);
}


			/*
			 * This routine returns the next run count from the pk
			 * character description. It will set the global repeat_count
			 * to any repeat count value encountered.
			 */
int
pk_packed_num()
{
	register int i, j;

	i = get_pk_nybble();
	if (i == 0) {
		do {
			j = get_pk_nybble();
			i++;
		} while (j == 0);
		while (i--)
			j = (j<<4) + get_pk_nybble();
		return (j - 15 + ((13 - dyn_f)<<4) + dyn_f);
	}
	else if (i <= dyn_f)
		return (i);
	else if (i < 14)
		return (((i - dyn_f - 1)<<4) + get_pk_nybble() + dyn_f + 1);
	else {
		if (i == 14)
			repeat_count = pk_packed_num();
		else		/* i must be 15 */
			repeat_count = 1;
		return (pk_packed_num());
	}
}

			/*
			 * This deals with run count encoded rasters.
			 * It uses pk_packed_num() which in turn uses get_pk_nybble().
			 */
void
normal_raster(dp)
short *dp;
{
	register int word = 0;
	register int count;
	register int h_bit = char_width;
	register int word_weight = 16;
	register int rows_left = char_height;

	repeat_count = 0;
	nybindex = 0;
	imageptr = dp;
	rowptr = row;

	while (rows_left > 0) {
		count = pk_packed_num();
		while (count > 0) {
			if ((count < word_weight) && (count < h_bit)) {
				if (turn_on)
					word += gpower[word_weight] - gpower[word_weight - count];
				h_bit -= count;
				word_weight -= count;
				count = 0;
			}
			else if ((count >= h_bit) && (h_bit <= word_weight)) {
				if (turn_on)
					word += gpower[word_weight] - gpower[word_weight - h_bit];
				*rowptr++ = word;
				send_row();
				rowptr = row;
				rows_left -= repeat_count + 1;
				repeat_count = 0;
				word = 0;
				word_weight = 16;
				count -= h_bit;
				h_bit = char_width;
			}
			else {
				if (turn_on)
					word += gpower[word_weight];
				*rowptr++ = word;
				word = 0;
				count -= word_weight;
				h_bit -= word_weight;
				word_weight = 16;
			}
		}
		turn_on ^= 1;
	}
	if ((rows_left != 0) || (h_bit != char_width)) {
		Fatal("Bad pk file - more bits than required\n");
		return;
	}
}


			/*
			 * This deals with bit mapped raster (dyn_f = 14).
			 * It uses get_pk_bit().
			 */
void
bitmap_raster(dp)
short *dp;
{
	register int i;
	register int j;
	register int word;
	register int word_weight;

	bit_weight = 0;

	for (i = 0; i < char_height; i++) {
		word = 0;
		word_weight = 15;
		for (j = 0; j < char_width; j++) {
			if (get_pk_bit())
				word += power[word_weight];
			if (word_weight-- == 0) {
				*dp++ = word;
				word = 0;
				word_weight = 15;
			}
		}
		if (word_weight < 15)
			*dp++ = word;
	}
}


			/*
			 * This reads the pk file preamble and rejects the file
			 * if it does not start correctly.
			 */
void
get_pk_preamble()
{
	register int comment_length;

	got_pk_post = FALSE;
	fseek(pkfp, 0L, 0);
	pk_loc = 0;
	if (get_pk_byte() != PK_PRE) {
		Fatal("Bad pk file - doesn't start with pk_pre\n");
		return;
	}
	if (get_pk_byte() != PK_ID) {
		Fatal("Bad pk file - wrong pk_id byte\n");
		return;
	}
	comment_length = get_pk_byte();
	while (comment_length--)
		get_pk_byte();
	(void)get_pk_double();
	(void)get_pk_double();
	(void)get_pk_double();
	(void)get_pk_double();
}


			/*
			 * This reads the preamble for one character up to the tfm width.
			 * The tfm width is read as when it is stored in the char_entry
			 * struct it needs to be multiplied by the font "s" parameter.
			 * ReadCharDefs() is passed a pointer to the font_entry struct
			 * so it can do this whereas LoadAChar() can't.
			 * The rest of the char will either be skipped or read with
			 * get_pk_char_dimen().
			 * flag_byte will have been set by a previous skip_specials().
			 */
void
get_pk_char_code()
{
	dyn_f = flag_byte >> 4;
	turn_on = (flag_byte & 0x8) >> 3;
	flag_byte &= 0x7;
	pk_char_start = pk_loc - 1; /* - 1 as flag byte has been read */

	if (flag_byte < 4) {	/* short preamble */
		packet_length = (flag_byte << 8) + get_pk_byte();
		char_code = get_pk_byte();
		tfm_width = get_pk_byte() << 16;
		tfm_width += get_pk_word();
		packet_length -= 3;	/* correct for reading tfm_width */
	}
	else if (flag_byte < 7) {	/* extended short preamble */
		packet_length = ((flag_byte - 4) << 16) + get_pk_word();
		char_code = get_pk_byte();
		tfm_width = get_pk_byte() << 16;
		tfm_width += get_pk_word();
		packet_length -= 3;
	}
	else {					/* long preamble */
		packet_length = get_pk_double();
		char_code = get_pk_double();
		tfm_width = get_pk_double();
		packet_length -= 4;
	}
}

			/*
			 * This reads the char preamble starting after the tfm width,
			 * carrying on where get_pk_char_code() left off.
			 */
void
get_pk_char_dimen()
{
	if (flag_byte < 4) {	/* short preamble */
		hor_esc = get_pk_byte();
		char_width = get_pk_byte();
		word_width = (char_width + 15)>>4;
		char_height = get_pk_byte();
		h_off = (char)get_pk_byte();
		v_off = (char)get_pk_byte();
	}
	else if (flag_byte < 7) {	/* extended short preamble */
		hor_esc = get_pk_word();
		char_width = get_pk_word();
		word_width = (char_width + 15)>>4;
		char_height = get_pk_word();
		h_off = (short)get_pk_word();
		v_off = (short)get_pk_word();
	}
	else {					/* long preamble */
		hor_esc = get_pk_double();
		vert_esc = get_pk_double();
		char_width = get_pk_double();
		word_width = (char_width + 15)>>4;
		char_height = get_pk_double();
		h_off = get_pk_double();
		v_off = get_pk_double();
	}
}


			/*
			 * This reads the preamble for one character.
			 */
void
get_pk_char_preamble()
{
	flag_byte = get_pk_byte();
	get_pk_char_code();
	get_pk_char_dimen();
}


/*
 * Now here are the routines called by the rest of textool
 */

/*-->FindFontFile*/

void
FindFontFile(tfontptr, mag)
struct font_entry *tfontptr;
unsigned int mag;
{
	char *direct, *tcp, *tcp1;
	int found;
	char curarea[STRSIZE];

	tfontptr->font_mag = (int)(
		ActualFactor((int)(((float)tfontptr->s/(float)tfontptr->d)*1000.0+0.5))
#ifdef USEGLOBALMAG
						* ActualFactor(mag)
#endif
						* (float)RESOLUTION + 0.5);
#ifdef DEBUG
	if (ExtraDebug) {
		fprintf(stderr, "FindFontFile():\n");
		fprintf(stderr, "  mag = %d\n", mag);
		fprintf(stderr, "  s = %d\n", tfontptr->s);
		fprintf(stderr, "  d = %d\n", tfontptr->d);
		fprintf(stderr, "  font_mag = %d\n", tfontptr->font_mag);
	}
#endif
	if (tfontptr->a != 0) {
		sprintf(tfontptr->name, "%s.%dpk", tfontptr->n, tfontptr->font_mag);
	} else {
		direct = PKpath;
		found = FALSE;
		do { 
			tcp = index(direct, ':');
			if (tcp == NULL) tcp = strlen(direct) + direct;
			strncpy(curarea, direct, tcp-direct);
			tcp1 = curarea + (tcp - direct);
			*tcp1++ = '/';
			*tcp1++ = '\0';

			sprintf(tfontptr->name, "%s%s.%dpk",
					curarea, tfontptr->n, tfontptr->font_mag);
			found = (access(tfontptr->name, R_OK) == 0);
			if (*tcp) direct = tcp + 1; else direct = tcp;
		} while ( !found && *direct != '\0');
	}
#ifdef DEBUG
	if (ExtraDebug)
		fprintf(stderr, "FindFontFile() returns with name %s\n",
				tfontptr->name);
#endif
}


/*-->LoadAChar*/

void
LoadAChar(ptr)
register struct char_entry *ptr;
{
	register Pixrect *pr;
	register short *dp;

	pkfp = OpenFontFile();
	if (ptr->where.address.fileOffset == 0) {
		ptr->where.address.pixrectptr = NULL;
		return;
	}
	fseek(pkfp, ptr->where.address.fileOffset, 0);
	get_pk_char_preamble();
	ptr->width = char_width;
	ptr->height = char_height;
	ptr->xOffset= h_off;
	ptr->yOffset = v_off;
	if ((pr = mem_create(ptr->width, ptr->height, 1)) == NULL) {
		Fatal("Couldn't allocate pixrect");
		return;
	}
	dp = ((struct mpr_data *)pr->pr_data)->md_image;
	if (dyn_f == 14)
		bitmap_raster(dp);
	else
		normal_raster(dp);
	ptr->where.address.pixrectptr = pr;
	ptr->where.isloaded = TRUE;
}


/*-->ReadCharDefs*/

void
ReadCharDefs(tfontptr, fp)
struct font_entry *tfontptr;
FILE *fp;
{
	register struct char_entry *tcharptr; /* temporary char_entry pointer */
	register Pixrect *pr;
	register int junk_count;
	short *dp;

#ifdef DEBUG
	if (ExtraDebug)
		fprintf(stderr, "ReadCharDefs() for font %s\n", tfontptr->name);
#endif
	pkfp = fp;
	get_pk_preamble();
	skip_specials();
	while (!got_pk_post) {
		get_pk_char_code();
		tcharptr = &(tfontptr->ch[char_code]);
		tcharptr->tfmw = tfm_width * (float)tfontptr->s / (float)(1<<20);
		if (BigPreLoad) {
			/*
			 * Load the rest of the character info.
			 */
			get_pk_char_dimen();
			tcharptr->width = char_width;
			tcharptr->height = char_height;
			tcharptr->xOffset= h_off;
			tcharptr->yOffset = v_off;
			if ((pr = mem_create(char_width, char_height, 1)) == NULL) {
				Fatal("Couldn't allocate pixrect");
				return;
			}
			dp = ((struct mpr_data *)pr->pr_data)->md_image;
			if (dyn_f == 14)
				bitmap_raster(dp);
			else
				normal_raster(dp);
			tcharptr->where.address.pixrectptr = pr;
			tcharptr->where.isloaded = TRUE;
		}
		else {
			tcharptr->where.isloaded = FALSE;
			tcharptr->where.address.fileOffset = pk_char_start;
			/*
			 * Otherwise skip the rest of the char.
			 * The bytes are read rather than using fseek as the pk files
			 * are small and the whole thing is likely to fit in one stdio
			 * buffer so this will not involve any function calls.
			 */
			junk_count = packet_length;
			while (junk_count--)
				getc(pkfp);
			pk_loc += packet_length;
		}
		skip_specials();
	}
}


