/* DVI2ps/src/newpk.c */

#ifndef NO_PK

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

/* This file contains routines for reading character bitmaps from PK files. */

/************* external definitions ****************/
#include <sys/types.h>
#include <stdio.h>
#include "pk.h"
#include "struct.h"
#include "site.h"
#include "defs.h"

extern void Fatal(), Warning();
extern char *malloc();

/************** external variables ****************/
extern FILE *outfp;
extern char *digit;
extern font_entry *fontptr;
extern int G_temporary;
#ifdef DEBUG
extern bool C_debug;
#endif DEBUG
#ifdef STATS
extern bool C_stats;
extern int Snbpxl, Sonbpx, Sndc;
#endif STATS


/********************************************************/
/*    First part: Initial Reading of Font Definition    */
/********************************************************/

/***************** forward definitions ******************/
void  ExtendedForm();
void  LongForm();
void  PKpreamble();
int   Scanpk();
void  ShortForm();
bool  SkipSpecials();
/****************** semiglobal variables ****************/
FILE *pkin;
font_entry *fp;
static int checksum;
u_int ii;
u_int cc;		/* character code */
long  pl;		/* packet length */
long  addr;		/* file offset */
u_int tfmw;		/* actual tfm width */

/********************************************************/
/*  Second Part: Decoding Character Raster Information  */
/********************************************************/

/***************** forward definitions ******************/
void  EmitPK();
int   GetNyb();
void  GetRaster();
void  Loadpkbits();
int   PackedNum();
void  UnpackRaster();
/****************** semiglobal variables ****************/
char_entry *cp;
int input_byte;
int bit_weight = 0;
int rep_count;
int dyn_f;

/****************************** ExtendedForm ***************/
void
ExtendedForm(c)
register unsigned c;
{
    pl = (long) UNSIGNED_2(ii) + (long) ((c & 0x03) << 16);
    cc = UNSIGNED_1;
    tfmw = UNSIGNED_3(ii);
    fp->ch[cc].dx = UNSIGNED_2(ii);
    fp->ch[cc].width = UNSIGNED_2(ii);
    fp->ch[cc].height = UNSIGNED_2(ii);
    fp->ch[cc].xOffset = SIGNED_2(ii);
    fp->ch[cc].yOffset = SIGNED_2(ii);
    addr = ftell(pkin);
    pl -= 13;
}

/********************************* LongForm ***********************/
void
LongForm()
{
    pl = UNSIGNED_4(ii);
    cc = UNSIGNED_4(ii);
#ifdef DEBUG
    if (C_debug)
	(void) fprintf(stderr, "%s -- Long character %d\n", fp->name, cc);
#endif DEBUG
    tfmw = UNSIGNED_4(ii);
    fp->ch[cc].dx = UNSIGNED_4(ii) >> 16;
    fp->ch[cc].dy = UNSIGNED_4(ii) >> 16;
    fp->ch[cc].width = UNSIGNED_4(ii);
    fp->ch[cc].height = UNSIGNED_4(ii);
    fp->ch[cc].xOffset = SIGNED_4(ii);
    fp->ch[cc].yOffset = SIGNED_4(ii);
    addr = ftell(pkin);
    pl -= 28;
}

/*************************** PKpreamble ************************/
void
PKpreamble()
{
    u_int hppp, vppp;

    (void) fseek(pkin, 0L, 0);	/* make sure at the beginning of pk file */
    if (UNSIGNED_1 != PK_PRE) {
	Fatal("dvi2ps: pk font file %s doesn't start with PRE\n", fp->name);
    }
    if (UNSIGNED_1 != PK_ID) {
	Fatal("dvi2ps: pk font file %s wrong version\n", fp->name);
    }
    (void) fseek(pkin, (long) UNSIGNED_1, 1);	/* skip comment */
    fp->designsize = UNSIGNED_4(ii);		/* ds[4] */
    checksum = UNSIGNED_4(ii);			/* checksum[4] */
    hppp = UNSIGNED_4(ii);			/* hppp[4] */
    vppp = UNSIGNED_4(ii);			/* vppp[4] */
    if (vppp != hppp) Warning("font = \"%s\" -- hppp != vppp", fp->name);
    fp->magnification = (int) (hppp * 72.27 / 65536 + 0.5);
}

/**************************** ScanPK ************************/
int
ScanPK(fntptr, pkfile)
font_entry *fntptr;
FILE *pkfile;
{
    double cscale;
    register unsigned c;

    pkin = pkfile;
    fp = fntptr;
    PKpreamble();
    cscale = (double) fp->s / (double) (1 << 20);
    while ((c = UNSIGNED_1) != EOF) {
	if (c >= PK_XXX1) {
	    if (SkipSpecials(c) == FALSE) return(checksum);
	} else {
	    switch (c & 0x07) {	/* check flag byte */
	    case 0: case 1: case 2: case 3:
		ShortForm(c); break;
	    case 4: case 5: case 6:
		ExtendedForm(c); break;
	    case 7:
		LongForm(); break;
	    }
	    fp->ch[cc].tfmw = (int) (((double) tfmw * cscale) + 0.5);
	    fp->ch[cc].where.isloaded = NOTLOADED;
	    fp->ch[cc].where.flags = c;
	    fp->ch[cc].where.address.fileOffset = addr;;
	    (void) fseek(pkin, pl, 1);	/* skip to next flag byte */
	}
    }
    return(checksum);
}

/**************************** ShortForm **************************/
void
ShortForm(c)
register unsigned c;
{
    pl = (long) UNSIGNED_1 + (long) ((c & 0x03) << 8);
    cc = UNSIGNED_1;
    tfmw = UNSIGNED_3(ii);
    fp->ch[cc].dx = UNSIGNED_1;
    fp->ch[cc].width = UNSIGNED_1;
    fp->ch[cc].height = UNSIGNED_1;
    fp->ch[cc].xOffset = SIGNED_1;
    fp->ch[cc].yOffset = SIGNED_1;
    addr = ftell(pkin);
    pl -= 8;

}

/**************************** SkipSpecials ************************/
bool
SkipSpecials(c)
register unsigned c;
{
    switch (c) {
    case PK_XXX1:	/* pk_xxx1 k[1] x[k] */
	(void) fseek(pkin, (long) UNSIGNED_1, 1);
	break;
    case PK_XXX2:	/* pk_xxx2 k[2] x[k] */
	(void) fseek(pkin, (long) UNSIGNED_2(ii), 1);
	break;
    case PK_XXX3:	/* pk_xxx3 k[3] x[k] */
	(void) fseek(pkin, (long) UNSIGNED_3(ii), 1);
	break;
    case PK_XXX4:	/* pk_xxx4 k[4] x[4] */
	(void) fseek(pkin, (long) SIGNED_4(ii), 1);
	break;
    case PK_YYY:	/* pk_yyy y[4] */
	(void) UNSIGNED_4(ii);
	break;
    case PK_POST:
	return (FALSE);
    case PK_PRE:
	(void) fprintf(stderr, "dvi2ps: pk font file %s has extra PRE\n",
	  fp->name);
	exit(1);
    default: /* do nothing */ ;
    }
    return(TRUE);
}


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

        EMITS("[<");
        cc = 2;
        nbpl = (ce->width + 7) >> 3;
        for(i = ce->height-1;  i >= 0;  i--) {
                sl = (unsigned char *) ce->where.address.pixptr + i*nbpl;
                for(j = 0;  j < nbpl;  j++, sl++) {
                        if( cc > 78 ) { EMITS("\n  ");   cc = 2; }
                        EMITH(*sl);
                        cc += 2;
                        }
                }
#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 ++;
#endif STATS
}


/******************************* GetNyb ********************************/
int
GetNyb()
{
    if (bit_weight==0) {
	input_byte = getc(pkin);
	bit_weight = 8;
    }
    return ((input_byte>>(bit_weight -= 4))&017);
}

/***************************** GetRaster ******************************/
void
GetRaster()
{
    register int ww, bw, w, j, i, c;
    register unsigned char *pr;
	pr = (unsigned char *) cp->where.address.pixptr;
	bw = 0;
	for (i=cp->height; i>0; i--) {
	    w = 0; ww = 0200;
	    for (j=cp->width; j>0; j--) {
		if ((bw>>=1)==0) { bw = 0200; c = getc(pkin);}
		if (c&bw) w |= ww;
		if ((ww>>=1)==0) {*pr++ = w; w = 0; ww = 0200;}
	    }
	    if (ww != 0200) *pr++ = w;
	}
}


/********************************* LoadPKbits *************************/
/* Load a PK character into memory. The data for the image can be packaged
 * either as a run-encoding where successive values are the number of 
 * adjacent pixels to paint in the opposite color of the previous painting, 
 * or simply as a bitmap with no padding except (possibly) for the very 
 * last nybble to round up to a byte value. The data for the character 
 * is stored in successive bits with each new horizontal row at a long 
 * boundary.  See the PXL file format.
 */
void 
LoadPKbits (pkfile, charptr)
FILE *pkfile;
char_entry *charptr;
{
    unsigned int  nbytes;
    unsigned char *sp;

    pkin = pkfile;
    cp = charptr;
    if (cp->width == 0 || cp->height == 0) {
	cp->where.address.pixptr = (char *) 0;
	return;
    }
    nbytes = ((cp->width + 7) >>3) * cp->height;
    sp = (unsigned char *) malloc (nbytes);
    if (! sp) 
	Fatal("dvi2ps: Loadpkbits: calloc failed.\n");
    cp->where.address.pixptr = (char *) sp;
    dyn_f = (cp->where.flags >> 4) & 0xf;
    if (dyn_f == 14)
	GetRaster();
    else
	UnpackRaster();
    cp->where.isloaded = G_temporary;
}

/*****************************  PackedNum  ******************************/
int
PackedNum()
{
    register int i, j;
    i = GetNyb();
    if (i==0) {
	do {j = GetNyb(); i++;} while (j==0);
	while (i-- > 0) 
	    j = (j<<4) + GetNyb();
	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)+GetNyb()+dyn_f+1);
    else {
	if (i==14) rep_count = PackedNum();
	else rep_count = 1;
	return (PackedNum());
    }
}
/**************************** UnpackRaster ****************************/

int gpower[] = {00,01,03,07,017,037,077,0177,0377};

void
UnpackRaster()
{
    char row[1000];
    register int ww, w, h_bit, count, r_l, r_p;
    register char turn_on;
    register unsigned char *pr;

    bit_weight = 0;
    turn_on = cp->where.flags & (1<<3);
    pr = (unsigned char *)cp->where.address.pixptr;
    r_l = cp->height;
    h_bit = cp->width;
    rep_count = 0;
    ww = 8; w = 0; r_p = 1;
    while (r_l > 0) {
	count = PackedNum();
	while (count > 0) {
	    if ((count<ww) && (count<h_bit)) {
		if (turn_on) w |= gpower[ww] - gpower[ww-count];
		h_bit -= count; ww -= count; count = 0;
	    } else if ((count>=h_bit) && (h_bit<=ww)) {
		if (turn_on) w |= gpower[ww] - gpower[ww-h_bit];
		row[r_p] = w;
		r_l -= (++rep_count);
		while (rep_count-- > 0) {
		    for (w=1; w<=r_p;) *pr++ = row[w++];
		}
		rep_count = 0; r_p = 1; w = 0; ww = 8;
		count -= h_bit; h_bit = cp->width;
	    } else {
		if (turn_on) w |= gpower[ww];
		row[r_p++] = w;
		w = 0; count -= ww; h_bit -= ww;
		ww = 8;
	    }
	}
	turn_on = ! turn_on;
    }
}

#endif NO_PK
