static char rcsid[] = "$Header: /usr/jjc/dvitops/RCS/pk.c,v 1.7 90/10/19 09:54:55 jjc Exp $";
 
#include "dvitops.h"
 
#define pk_id 89
 
#if 0 
enum pk_command { pk_xxx1 = 240, pk_xxx2 = 241, pk_xxx3 = 242, pk_xxx4 = 243,
pk_yyy = 244, pk_post = 245, pk_no_op = 246, pk_pre = 247 };
#else
#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_no_op  246
#define pk_pre  247
 
#endif
static struct {
	integer llx, lly, urx, ury;
} bb; /* bounding box for the entire font */
#ifdef PROTO
static void out_byte(PROMOTED_UNSIGNED_CHAR c, FILE *psfp);
static void blacken(integer start, integer n, unsigned char *buf);
static integer packed_num(int dyn_f);
static void bitmap_char(integer h, integer w, FILE *psfp);
static integer makeinteger(unsigned char *p);
static int generate_char(PROMOTED_UNSIGNED_CHAR flag, unsigned char *data,
						 size_t len, FILE *psfp);
#else
static void out_byte();
static void blacken();
static integer packed_num();
static void bitmap_char();
static integer makeinteger();
static int generate_char();
#endif
 
static struct pk_info {
	integer ds;
	integer hppp;
	integer vppp;
	unsigned char flag[MAXCHARS];
	size_t len[MAXCHARS];
	unsigned char *data[MAXCHARS];
} *pk_table[MAXFONTS];
 
static integer nbytes;
 
char *find_pk_file(name, mag)
char *name;
int mag;
{
	FILE *pkfp;
	int i;
	static char buf[10*FILENAME_MAX];
	char *ptr = buf;
	static char sep[2] = {AREA_LIST_SEP, '\0'};
	for (i = 0; texpk[i] != '\0'; i++)
		if (texpk[i] == '%') {
			switch(texpk[i+1]) {
			case 'f': case 'F':
				strcpy(ptr, name);
				break;
			case 'd': case 'D':
				sprintf(ptr, "%d", mag);
				break;
			default:
				sprintf(ptr, "%c", texpk[i+1]);
			}
			++i;
			ptr += strlen(ptr);
		}
		else
			*ptr++ = texpk[i];
	*ptr = '\0';
	for (ptr = strtok(buf, sep); ptr != NULL; ptr = strtok((char *)NULL, sep)) {
		if ((pkfp = FOPEN_RB(ptr)) != NULL) {
			fclose(pkfp);
			return ptr;
		}
	}
	return NULL;
}


int load_pk_font(f, name, used, at_size, checksum, width, rounded_width)
int f;
char *name, *used;
integer at_size, checksum, *width, *rounded_width;
{
	FILE *pkfp;
	integer n;
	integer cs;
	double sppp;
	int i;
	if ((pkfp = FOPEN_RB(name)) == NULL) {
		message(ERROR, 
			"can't find pk file for %s: using white space instead",	name);
		return 0;
	}
#ifdef HAVE_SETVBUF
	setvbuf(pkfp, (char *)NULL, _IOFBF, 8192);
#endif
	if ((pk_table[f] = (struct pk_info *)
						calloc(1, sizeof(struct pk_info))) == NULL)
		out_of_memory();
	if (uread1(pkfp) != pk_pre)
		goto bad;
	if (uread1(pkfp) != pk_id)
		goto bad;
	n = uread1(pkfp);
	fseek(pkfp, n, 1);
	pk_table[f]->ds = sread4(pkfp);
	cs = sread4(pkfp);
	pk_table[f]->hppp = sread4(pkfp);
	pk_table[f]->vppp = sread4(pkfp);
	/* sppp = scaled points per pixel
		sppp*dx = width in scaled points */
	sppp = (double)at_size*1048576.0*65536.0
				/((double)pk_table[f]->ds*(double)pk_table[f]->hppp);
 
	if (cs != checksum)
		message(WARNING, "%s: checksums don't match\n", name);
	for (;;) {
		unsigned char command = uread1(pkfp);
		if (feof(pkfp) || ferror(pkfp))
			goto bad;
		if (((unsigned)command >> 4) == 15) {
			switch (command) {
			case pk_xxx1 :
				n = uread1(pkfp); break;
			case pk_xxx2 :
				n = uread2(pkfp); break;
			case pk_xxx3 :
				n = uread3(pkfp); break;
			case pk_xxx4 :
				n = sread4(pkfp); break;
			case pk_yyy  :
				(void) sread4(pkfp); break;
			case pk_post :
				for (i = 0; i < MAXCHARS; i++)
					if (used[i] && width[i] == (integer) LONG_MAX)
						goto bad;
				fclose(pkfp);
				return 1;
			case pk_no_op :
				break;
			default:
				goto bad;
			}
			if (pk_xxx1 <= command && command <= pk_xxx4)
				fseek(pkfp, n, 1);
		}
		else {
			integer cc, pl;
			integer tfm;
			double dx;
			unsigned char c;
			size_t len;
			unsigned char flag = command;
			command &= 0x07;
			if (command == 0x07) {
				pl = sread4(pkfp);
				cc = sread4(pkfp);
				tfm = sread4(pkfp);
				dx = (double)sread4(pkfp)/65536.0;
				fseek(pkfp, -4L, 1);
				pl -= 4;
			}
			else if (command & 0x04) {
				pl = uread2(pkfp);
				pl |= (integer)(command & 0x03) << 16;
				cc = uread1(pkfp);
				tfm = uread3(pkfp);
				dx = (double)uread2(pkfp);
				fseek(pkfp, -2L, 1);
				pl -= 3;
			}
			else {
				pl = uread1(pkfp);
				pl |= (command & 0x03) << 8;
				cc = uread1(pkfp);
				tfm = uread3(pkfp);
				dx = (double)uread1(pkfp);
				fseek(pkfp, -1L, 1);
				pl -= 3;
			}
			if (cc < 0 || cc >= MAXCHARS) {
				fseek(pkfp, pl, SEEK_CUR);
				continue;
			}
			c = (unsigned char)cc;
			if (!used[c]) {
				pk_table[f]->data[c] = NULL;
				fseek(pkfp, pl, SEEK_CUR);
			}
			else {
				if (pl < 0 || pl > SIZE_MAX)
					goto bad;
				len = (size_t)pl;
				width[c] = scale(tfm, at_size);
				rounded_width[c] = (integer)(dx*sppp);
				pk_table[f]->flag[c] = flag;
				pk_table[f]->len[c] = len;
				if ((pk_table[f]->data[c] = (unsigned char *)malloc(len))
								== NULL)
					out_of_memory();
				if (fread((char *)pk_table[f]->data[c], 1, len, pkfp) != len)
					goto bad;
			}
		}
	}
	/* can't get here */
bad:
	message(ERROR, "%s: bad pk file: using white space instead", name);
	return 0;
}
 
static unsigned char *ptr;
static char biti;
static integer rc;
 
#define get_nyb() (biti == 0 ? (biti=4, (unsigned)*ptr>>4) \
: (biti = 0, *ptr++ & 0x0f))
 
 
static integer packed_num(dyn_f)
int dyn_f;
{
	integer i, j;
	integer n;
	i = get_nyb();
	if (i == 0) {
		do {
			 j = get_nyb();
			 i++;
		} while (j == 0);
		while (i > 0) {
			 j = (j << 4) + get_nyb();
			 i--;
		}
		n = (j - 15 + ((13 - dyn_f) << 4) + dyn_f);
	}
	else if (i <= dyn_f)
		n = i;
	else if (i < 14)
		n = ((i - dyn_f - 1) << 4) + get_nyb() + dyn_f + 1;
	else {
		rc = i == 14 ? packed_num(dyn_f) : 1;
		n = packed_num(dyn_f);
	}
	return n;
}
 
/* this can be made a lot more efficient later */
 
static void blacken(start, n, buf)
integer start, n;
unsigned char *buf;
{
	integer i;
	for (i = start; i < start + n; i++)
		buf[(size_t)(i/8)] |= (unsigned)128 >> (int)(i & 7);
}
 
 
static char *hex = "0123456789ABCDEF";
 
static void out_byte(c, psfp)
unsigned char c;
FILE *psfp;
{
	static int cols = 0;
	putc(hex[(unsigned)(c) >> 4],psfp);
	putc(hex[(c) & 15],psfp);
	if ((cols += 2) > 70) {
		cols = 0;
		putc('\n', psfp);
	}
	++nbytes;
}
 
/* postscript requires that the bitmap of each row be padded out to an
integral number of bytes, so unfortunately we can't just copy the bitmap
in the pk file; this function could probably be made faster, but most
of the characters that pk format represents by bitmaps are fairly small */
 
static void bitmap_char(h, w, psfp)
integer h, w;
FILE *psfp;
{
	integer i, j;
	unsigned mask = 0x80;
	unsigned char curbyte = 0;
	unsigned curmask = 0x80;
	for (i = 0; i < h; i++)
		for (j = 0; j < w; j++) {
			if (*ptr & mask)
				curbyte |= curmask;
			if ((mask >>= 1) == 0) {
				mask = 0x80;
				ptr++;
			}
			if ((curmask >>= 1) == 0 || j == w - 1) {
				out_byte(curbyte, psfp);
				curbyte = 0;
				curmask = 0x80;
			}
		}
/* the value of ptr is checked on return to make sure we have used the right
number of bytes */
	if (mask != 0x80) 
		ptr++;
}
 
static integer makeinteger(p)
unsigned char *p;
{
	return (((integer)p[0]<<24) | ((integer)p[1]<<16) | (p[2]<<8) | p[3]);
}
 
/*
output strings have one of the following forms:
(4 byte integers are always signed)
flag[1] dm[1] w[1] h[1] hoff[+1] voff[+1] flag = 0
flag[1] dm[2] w[2] h[2] hoff[+2] voff[+2] flag = 1
flag[1] dx[4] dy[4] w[4] h[4] hoff[4] voff[4] flag = 2
*/
 
/* returns 0 on error != 0 otherwise */
static int generate_char(flag, data, len, psfp)
unsigned char flag;
unsigned char *data;
size_t len;
FILE *psfp;
{
	int black = (flag & 0x08) != 0;
	integer h, w;
	int dyn_f;
	integer hoff, voff;
	integer curcol, currow;
	unsigned char *rowbuf;
	size_t buflen;
	dyn_f = (unsigned)flag >> 4;
	ptr = data;
	rc = 0;
	biti = 0;
	fputc('<',psfp);
	if ((flag & 0x07) == 0x07) {
		int i;
		out_byte(2, psfp);
		for (i = 0; i < 24; i++)
			out_byte(ptr[i], psfp);
		ptr += 8;
		w = makeinteger(ptr); ptr += 4;
		h = makeinteger(ptr); ptr += 4;
		hoff = makeinteger(ptr); ptr += 4;
		voff = makeinteger(ptr); ptr += 4;
	}
	else if (flag & 0x04) {
		int i;
		out_byte(1, psfp);
		for (i = 0; i < 10; i++)
			out_byte(ptr[i], psfp);
		ptr += 2;
		w = (ptr[0] << 8) | ptr[1]; ptr += 2;
		h = (ptr[0] << 8) | ptr[1]; ptr += 2;
		hoff = *ptr++;
		if (hoff >= 128)
			hoff -= 256;
		hoff <<= 8;
		hoff |= *ptr++;
		voff = *ptr++;
		if (voff >= 128)
			voff -= 256;
		voff <<= 8;
		voff |= *ptr++;
	}
	else {
		int i;
		out_byte(0, psfp);
		for (i = 0; i < 5; i++)
			out_byte(ptr[i], psfp);
		ptr++;
		w = *ptr++;
		h = *ptr++;
		hoff = *ptr++;
		if (hoff >= 128)
			hoff -= 256;
		voff = *ptr++;
		if (voff >= 128)
			voff -= 256;
	}
	if (-hoff < bb.llx)
		bb.llx = -hoff;
	if (w-hoff > bb.urx)
		bb.urx = w-hoff;
	if (voff + 1 - h < bb.lly)
		bb.lly = voff + 1 - h;
	if (voff + 1 > bb.ury)
		bb.ury = voff + 1;
	if (dyn_f == 14) {
		bitmap_char(h, w, psfp);
		if (data + len != ptr)
			return 0;
		fputc('>',psfp);
		return 1;
	}
	currow = curcol = 0;
	buflen = (size_t)((w + 7) / 8);
	if (buflen == SIZE_MAX) /* we'd get an infinite loop if we allowed this */
		return 0;
	rowbuf = (unsigned char *)calloc(1, buflen);
	if (rowbuf == NULL)
		out_of_memory();
	while  (currow < h) {
		integer run = packed_num(dyn_f);
		while (curcol + run >= w) {
			size_t i;
			integer j;
			if (black)
				blacken(curcol, w - curcol, rowbuf);
			for (j = 0; j <= rc; j++) {
				for (i = 0; i < buflen; i++)
					out_byte(rowbuf[i],psfp);
			}
			for (i = 0; i < buflen; i++)
				rowbuf[i] = 0;
			currow++; currow += rc; rc = 0;
			run -= w - curcol;
			curcol = 0;
		}
		if (black)
			blacken(curcol, run, rowbuf);
		curcol += run;
		black = !black;
	}
	if (biti > 0)
		ptr++;
	if (data + len != ptr)
		return 0;
	fputc('>',psfp);
	free((char *)rowbuf);
	return 1;
}
 
	
integer emit_pk_font(f, name, used, psfp)
int f;
char *name;
char *used;
FILE *psfp;
{
	int i, nused;
	nbytes = 0;
	if (pk_table[f] == NULL)
		cant_happen();
	fprintf(psfp, "%%%%BeginFont: %s\n", name);
	fprintf(psfp, "/%s %ld %ld %ld\n", name, (long)(pk_table[f]->ds),
			(long)(pk_table[f]->hppp), (long)(pk_table[f]->vppp));
	bb.llx = bb.lly = bb.urx = bb.ury = 0;
	nused = 0;
	fputs("EmptyEncoding 256 array copy\n", psfp);
	for (i = 0; i < MAXCHARS; i++)
		if (used[i]) {
			fprintf(psfp, "dup %d /c%d put\n", i, i);
			++nused;
		}
	/* allow room for /.notdef */
	fprintf(psfp, "\n%d dict dup begin\n", nused+1);
	for (i = 0; i < MAXCHARS; i++)
		if (used[i]) {
			if (pk_table[f]->data[i] == NULL)
				cant_happen();
			fprintf(psfp, "/c%d", i);
			if (!generate_char(pk_table[f]->flag[i], pk_table[f]->data[i],
					pk_table[f]->len[i], psfp))
				message(FATAL_ERROR, 
					"char %d in the .pk file for %s is bad: use pktype",
					i, name);
			fputs("def\n", psfp);
		}
	fprintf(psfp, "end %ld %ld %ld %ld DefinePKFont\n", (long)(bb.llx),
		(long)(bb.lly), (long)(bb.urx), (long)(bb.ury));
	fputs("%%EndFont\n", psfp);
	/* this is a fairly good estimate of the overhead for each font */
	return nbytes + 1024 + 2000 + 20*(nused+1); 
	/* nbytes for the strings, 1024 for the encoding vector,
		20 for each of the nused+1 entries in the dictionary
		plus 2000 for general overhead */
}
 
 
 
 
void free_pk_font(n)
int n;
{
	int i;
	struct pk_info *p = pk_table[n];
	if (pk_table[n] == NULL)
		cant_happen();
	for (i = 0; i < MAXCHARS; i++)
		if (p->data[i] != NULL)
			free((char *)(p->data[i]));
	free((char *)p);
	pk_table[n] = NULL;
}

/*
Local Variables:
c-indent-level: 4
c-continued-statement-offset: 4
c-brace-offset: -4
c-argdecl-indent: 0
c-label-offset: -4
tab-width: 4
End:
*/

