/*
 *	This file is based on `texpr/dvi2lpb/pkfont.c'.
 *	The copyright of the original is:
 *		Copyright (c) Japan TeX Users Group, 1986 1987
 */

#include	"defs.h"
#include	"global.h"
#include	"rastfont.h"
#include	"pkfont.h"
#ifdef KPATHSEA
#undef STREQ
#include	<kpathsea/config.h>
#include	<kpathsea/tex-file.h>
#endif

int pktype_access();
void init_pk_fontinfo();
struct fontop pkop = {
    "pk",
    pathtype_init,
    pktype_access,
    init_pk_fontinfo,
};

static FILE *fntfp;
void loadpkchar();

pktype_access(proto, fe, acca)
char *proto;
struct font_entry *fe;
struct accarg *acca;
{
    char *filename;
    BOOLEAN ok;

    rast_mag(fe, acca, 1);
#ifdef KPATHSEA
    if (ok = ((filename = kpsearch_glyph(proto, fe->n, kpse_pk_format,
					 acca, fe->name)) != NULL))
	strcpy(fe->name, filename);
#else
    pave(fe->name, proto, acca);
    ok = access(fe->name, R_OK) == 0;
#endif
#ifdef DEBUG
    rast_debug_report(fe, acca, ok, "pk");
#endif
    return ok;
}

void
init_pk_fontinfo(fe)
struct font_entry *fe;
{
    void read_pk_fontinfo();

    fe->fnt_readfontinfo = read_pk_fontinfo;
    init_rast_fontinfo(fe);
}

void
read_pk_fontinfo(fe)
struct font_entry *fe;
{
    int cmd, i, t;
    struct rastinitfontinfo *rii;
    struct rastfntinfo *rfi;
    int ds;

    openfontfile(fe);
    fntfp = fe->openfile;
    i = 0;
    do {
	if (fseek(fntfp, (long)--i, SEEK_END) == -1)
	    Fatal("can't seek to the end of pk file %s\n", fe->name);
    } while ((t=getuint(fntfp, 1)) == PK_NO_OP);
    (void)fseek(fntfp, 0L, SEEK_SET);
    if (t != PK_POST ||
	getuint(fntfp,1) != PK_PRE || getuint(fntfp,1) != PK_ID)
	Fatal("bad pk file %s\n", fe->name);
    (void)fseek(fntfp, (long)getuint(fntfp, 1), SEEK_CUR);	/* comment */
    ds = getuint(fntfp, 4);	/* design size */
    if ((t = getuint(fntfp, 4)) && fe->c && t != fe->c)
	Warning("font = \"%s\",\n-->font checksum = %d,\n-->dvi checksum = %d",
		fe->name, fe->c, t);
    (void)fseek(fntfp, 8L, SEEK_CUR);		/* hppp,vppp */

    rii = rastinifinfo(fe);
    rastfinfo(fe) = rfi = alloc_rastfinfo(rii->maxc+1, TRUE, rii);
#ifdef DEBUG
    if (Debug) {
	for (i = 0; i <= rii->maxc; i++)
	    rastfinfo(fe)->ch[i].where.pixptr = NULL;
    }
#endif
    dev_rast_initfe(fe);
    /* rfi->designsize = ds; */
    rfi->nfntchars = rii->maxc + 1;
    for (;;) {
	switch (cmd = getuint(fntfp, 1)) {

	case PK_XXX1: case PK_XXX2: case PK_XXX3: case PK_XXX4:
	    (void)fseek(fntfp, (long)getuint(fntfp, cmd-PK_XXX1+1), SEEK_CUR);
	    continue;

	case PK_YYY:
	    skipbytes(fntfp, 4);
	    continue;

	case PK_NO_OP:
	    continue;

	case PK_POST:
	    break;

	default:
	    if (cmd <= PK_FLAG)
		readpkchar(cmd, fe, rii);
	    else
		Fatal("illegal pk command %d in %s\n", cmd, fe->name);
	    continue;
	}
	break;
    }

#ifdef DEBUG
    if (Debug) {
	for (i = 0; i <= rii->maxc; i++)
	    if (rii->mark[i] && rastfinfo(fe)->ch[i].where.pixptr == NULL)
		(void)fprintf(stderr, "The glyf of char %x in %s missing\n",
			      i, fe->name);
    }
#endif
    free((char *)rii);
}

readpkchar(flag, fe, rii)
int flag;
struct font_entry *fe;
struct rastinitfontinfo *rii;
{
    long pl;
    unsigned int cc;
    int n;
    struct rastchar_entry *ce;	/* temporary char_entry pointer  */

    if ((n = flag&7) == 7) {	/* long form */
	pl = getuint(fntfp, 4) - 8;	/* without tfmw,dx */
	if ((cc = getuint(fntfp, 4)) > MAXMARKCHAR) {
	    Warning("cc %d in pk file %s (skipped)", cc, fe->name);
	    (void)fseek(fntfp, pl+8, SEEK_CUR);
	    return;
	}
	if (rii->mark[cc] == FALSE) {
	    (void)fseek(fntfp, pl+8, SEEK_CUR);
	    return;
	}
	ce = &rastfinfo(fe)->ch[cc];
	ce->tfmw = scale(getuint(fntfp, 4), fe->s);
	skipbytes(fntfp, 4); 	/* dx */
	n = 4;
    } else if (n >= 4) {		/* extended short form */
	pl = (n-4)*65536 + getuint(fntfp, 2) - 3;	/* without tfmw */
	ce = &rastfinfo(fe)->ch[cc = getuint(fntfp, 1)];
	if (rii->mark[cc] == FALSE) {
	    (void)fseek(fntfp, pl+3, SEEK_CUR);
	    return;
	}
	ce->tfmw = scale(getuint(fntfp, 3), fe->s);
	n = 2;
    } else {			/* short form */
	pl = n*256 + getuint(fntfp, 1) - 3;	/* without tfmw */
	ce = &rastfinfo(fe)->ch[cc = getuint(fntfp, 1)];
	if (rii->mark[cc] == FALSE) {
	    (void)fseek(fntfp, pl+3, SEEK_CUR);
	    return;
	}
	ce->tfmw = scale(getuint(fntfp, 3), fe->s);
	n = 1;
    }
    skipbytes(fntfp, n); 		/* dy or dm */
    ce->width = getuint(fntfp, n);
    ce->height = getuint(fntfp, n);
    ce->xoffset = getint(fntfp, n);
    ce->yoffset = getint(fntfp, n);
    ce->nbpl = (unsigned short)(ce->width + 7) >> 3;

    loadpkchar(fe, ce, flag, (long)(pl-5*n));
    dev_rast_initfontdict(fe, cc);
}

static int	width, height, dyn_f;
static int	b_width, black;
static unsigned char *pixel;
static unsigned char pat[8] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};

/*	character shape definition	*/
/* ARGSUSED */
void
loadpkchar(fe, ce, flag, pl)
struct font_entry *fe;
struct rastchar_entry *ce;
int flag;
long pl;
{
    dyn_f = flag/16;
    black = (flag&8) >> 3;
    width = ce->width;
    height = ce->height;
    b_width = ce->nbpl;
    if ((pixel = (unsigned char *)calloc((unsigned)height, (unsigned)b_width)) == NULL)
	Fatal("Unable to allocate memory for char\n");
    if (dyn_f == 14)
	unpack_raster();
    else
	unpack_run(pl);
    ce->where.pixptr = (char *)pixel;
}

/*	get nybble	*/

#define get_nyb()  ((nyb_f^=1)? (nyb_w=getuint(fntfp,1))/16: nyb_w&15)

/*	get run_count/repeat_count	*/

#define get_run()\
	for (;;) {\
		if ((i=get_nyb()) >= 14) {\
			rep = i*2-29;\
			continue;\
		} else if (i > dyn_f)\
			run = (i-dyn_f-1)*16 + get_nyb() + dyn_f + 1;\
		else if (i > 0)\
			run = i;\
		else /* i==0 */ {\
			do	++i;\
			while ((run = get_nyb()) == 0);\
			while (--i >= 0)\
				run = run*16 + get_nyb();\
			run += (13-dyn_f)*16 + dyn_f - 15;\
		}\
		if (rep >= 0)\
			break;\
		rep = run;\
	}\

#define	min(x,y)	((x)<=(y) ? (x) : (y))

/*	unpack run_encoded packet	*/

unpack_run(pl)
long pl;
{
    unsigned char *p, *q;
    int i, run, rep, h, h1, v;
    int	nyb_f, nyb_w;
    long off;

    nyb_f = 0;
#ifdef lint
    nyb_w = 0;
#endif
    p = pixel;
    black ^= 1;
    run = 0;
    off = ftell(fntfp);
    for (v=0; v < height; v += rep+1) {
	rep = 0;
	for (h=0; h < width; h=h1) {
	    if (run == 0) {
		get_run();
		black ^= 1;
	    }
	    h1 = min(h+run, width);
	    if (black) {
		h1 = min(h1, (h+8)&~7);
		p[h/8] |= pat[h&7];
		if(h1 & 7)
		    p[h1/8] &= ~pat[h1&7];
	    }
	    run -= h1-h;
	}
	q = p;	p += b_width;
	for (h=rep*b_width; --h >= 0; )
	    *p++ = *q++;
    }
    (void)fseek(fntfp, off+pl, SEEK_SET);
}

/*	unpack raster packet	*/

unpack_raster()
{
    unsigned char *p;
    int n, m, t, h, v;

    p = pixel;
    n = 0;
    for (v=0; v < height; ++v) {
	for (h=0; h < width; h+=m) {
	    if (n == 0) {
		t = getuint(fntfp, 1);
		n = 8;
	    }
	    m = min(n, 8-(h&7));
	    m = min(m, width-h);
	    p[h/8] |= t >> (h&7);
	    t <<= m; n -= m;
	}
	if (width & 7)
	    p[width/8] &= ~pat[width&7];
	p += b_width;
    }
}
