/*
 * Copyright 1989 Dirk Grunwald
 * 
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of Dirk Grunwald or M.I.T.
 * not be used in advertising or publicity pertaining to distribution of
 * the software without specific, written prior permission.  Dirk
 * Grunwald and M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 * 
 * DIRK GRUNWALD AND M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL M.I.T.  BE LIABLE FOR ANY SPECIAL, INDIRECT
 * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
 * OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * Author:
 * 	Dr. Dirk Grunwald
 * 	Dept. of Computer Science
 * 	Campus Box 430
 * 	Univ. of Colorado, Boulder
 * 	Boulder, CO 80309
 * 
 * 	grunwald@colorado.edu
 * 	
 */ 

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "dvistuff.h"	/* includes types & fonts */

#include "dvi.h"
#include "dviclass.h"
#include "dvicodes.h"
#include "postamble.h"
#include "search.h"
#include "fio.h"
#include "proto.h"

int	dviHH = -1;		/* current horizontal position, in DEVs */
int	dviVV = -1;		/* current vertical position, in DEVs */
long	*dviCount[DVI_COUNT];	/* values of count */
DviStack dvi_current;
int	dviHHMargin = -1;	/* horizontal margin (in DEVs) */
int	dviVVMargin = -1;	/* vertical margin (in DEVs) */
int	dviTallestPage = -1;
int	dviWidestPage = -1 ;
int	dviCurrentPage = -1;
int	dviTotalPages = -1;
int	dviDPI = -1;
int	dviMaxDrift = -1;
int	dviUserMag = -1;	/* user-specified magnification */
int	dviBlackness = -1;
int	dviFontRotation = -1;
char	*dviFileName;
static char *dviTmpFileName = 0;
static char dviTmpFileNameBuffer[256];
FILE	*dviFile;				/* users file */
char	*dviPrintEngine = "canon";

char	*ProgName;

/*
 * Similar to dvi_stack, but includes `dviHH and `dviVV, which are usually
 * but not always the same as fromSP(h) and fromSP(v):
 */
struct localstack {
	int stack_hh;
	int stack_vv;
	struct dvi_stack stack_dvi;
};

static struct localstack *dvi_stack = 0;/* base of stack */
static struct localstack *dvi_stackp;	/* current place in stack */

/*
 * DVI preamble and postamble information.
 */

static long Magnification;
static long	Numerator;		/* numerator from DVI file */
static long	Denominator;		/* denominator from DVI file */

/*
 *	Font related things.
 *	We keep track of the current font, use the fontfiner
 *	and record errors in the dvi postable in FontErros. 
 *	the NoFont is a used when we cant find a font,
 *	to avoid errors.
 */

struct fontinfo *dviCurrentFont = 0;

/*
 *	imagen1-special uses the NextFamilyNumber. *sigh*
 */

int NextFamilyNumber = 0;
static int MaxFontFamily = MAX_FONTFAMILY;

static struct search *FontFinder = 0;
int FontErrors = 0;
static struct fontinfo NoFont;
/*
 *	We keep a list of pointers to the beginning of pages.
 *	Each page is headed by a DVI_BOP + 44 bytes of information.
 */

static long *pageOffset = 0;

/*
 * Correct devpos (the actual device position) to be within MaxDrift pixels
 * of dvipos (the virtual DVI position).
 */
#define FIXDRIFT(devpos, dvipos) \
	if (ABS((devpos) - (dvipos)) <= dviMaxDrift) \
		/* void */; \
	else \
		if ((devpos) < (dvipos)) \
			(devpos) = (dvipos) - dviMaxDrift; \
		else \
			(devpos) = (dvipos) + dviMaxDrift

#define	ABS(X)	( (X) < 0 ? -(X) : (X) )


/*
 * Store the relevant information from the DVI postamble, and set up
 * various internal things.
 */
static void
PostAmbleHeader(p)
    register struct PostAmbleInfo *p;
{
    int i;
    int page;
    int pageSize;
    int stackSize;
    long prevPagePointer;
    long wuzAt;
    
    Numerator = p->pai_Numerator;
    Denominator = p->pai_Denominator;
    Magnification = p->pai_DVIMag;
    
/*
 *	Set the conversion factor.  This must be done before using
 *	any fonts or the fromSP routine.
 */
    
    SetConversion(dviDPI, dviUserMag, Numerator, Denominator, Magnification);
    
    dviTotalPages = p -> pai_NumberOfPages;
    dviWidestPage = fromSP(p -> pai_WidestPageWidth);
    dviTallestPage = fromSP(p -> pai_TallestPageHeight);

/*
 *	Set up the DVI stack
 */
    stackSize = p -> pai_DVIStackSize * sizeof(*dvi_stack);
    if (dvi_stack != 0) {
	free(dvi_stack);
    }
    dvi_stack = (struct localstack *) malloc((unsigned) stackSize);  

/*
 *	Set of the table of pointers to pages and room for the
 *	count variables.
 */
    
    pageSize = dviTotalPages * sizeof(long);
    if (pageOffset != 0) {
	free(pageOffset);
    }
    pageOffset = (long *) malloc(pageSize);
    
    if (pageOffset == NULL) {
	fprintf(stderr,"xdvi: Can not allocate page directory (%d pages)",
		dviTotalPages);
	exit(1);
    }

    for (i = 0; i < DVI_COUNT; i++) {
	if (dviCount[i] != 0) {
	    free(dviCount[i]);
	}
	dviCount[i] = (long*) malloc(pageSize);
	if (dviCount[i] == NULL) {
	    fprintf(stderr,"xdvi: Can not allocate count directory (%d pages)",
		    dviTotalPages);
	    exit(1);
	}
    }
	    
    
/*
 * Follow back pointers through pages in the DVI file,
 * storing the offsets in the pageOffset table.
 */

    prevPagePointer = p->pai_PrevPagePointer;
    wuzAt = (long) ftell(dviFile);
    
    for (page = dviTotalPages - 1; page >= 0 ; page--) {

/*
 *	Skip to previous page, but skip over BOP marker.
 */
	pageOffset[page] = prevPagePointer;
	if( fseek(dviFile, prevPagePointer+1, 0) < 0) {
	    perror("fseek");
	    fprintf(stderr,"[postamble] improper seek looking up pages\n");
	    fprintf(stderr,"prevPagePointer = %lx\n",
		    prevPagePointer);
	    exit(1);
	}

/*
 *	Read the other counters at the beginning of the page
 */

	for (i = 0; i < DVI_COUNT; i++) {
	    long value;
	    fGetLong(dviFile, value);
	    dviCount[i][page] = value;
	    }
	fGetLong(dviFile, prevPagePointer);
    }
    fseek(dviFile, wuzAt, 0);
}

static void
PostAmbleFontDef(p)
register struct PostAmbleFont *p;
{
    register struct fontinfo *fi;
    register struct font *f;
    
    char *fname;
    int def = S_CREATE | S_EXCL;
    
    fi = (struct fontinfo *) SSearch(FontFinder, p->paf_DVIFontIndex,
				     &def);
    if (fi == NULL) {
/*
 *	If the font already exists, lets just check that its really the
 *	same thing & return
 */

	if ((def & S_FOUND) | (def & S_COLL)) {

	    def = S_LOOKUP;
	    if ((fi = (struct fontinfo *) SSearch(FontFinder,
						  p -> paf_DVIFontIndex,
						  &def)) == 0){
		GripeNoSuchFont(p -> paf_DVIFontIndex);
		exit(1);
	    }

	    if (fi -> f == NULL) {
		GripeCannotGetFont(p->paf_name, p->paf_DVIMag,
				   p->paf_DVIDesignSize,
				   dviPrintEngine, fname);
	    }
	    f = fi -> f;
	    if (p->paf_DVIChecksum && f->f_checksum &&
		p->paf_DVIChecksum != f->f_checksum) {
		    GripeDifferentChecksums(fname, p->paf_DVIChecksum,
					    f->f_checksum);
		    exit(1);
		}
	    return;	/* font already defined */
	}
	else {
	    fprintf(stderr, "Can not stash font %ld (out of memory?)\n",
		    p -> paf_DVIFontIndex);
	    exit(1);
	}
    }

    if (NextFamilyNumber == MaxFontFamily) {
	fprintf(stderr,"Out of font family!\n");
	exit(1);
    }

    fi->family = NextFamilyNumber;
    NextFamilyNumber++;

    f = GetFont(p->paf_name, p->paf_DVIMag, p->paf_DVIDesignSize,
		dviPrintEngine, &fname);

/*
 *	Give the application a chance to bang on this if they want to.
 */

    f = applicationNewFont(f, fi -> family);

    if ((fi->f = f) == NULL) {
	GripeCannotGetFont(p->paf_name, p->paf_DVIMag,
			   p->paf_DVIDesignSize, dviPrintEngine, fname);
	FontErrors++;
	return;
    }

    /* match checksums, if not zero */
    if (p->paf_DVIChecksum && f->f_checksum &&
	p->paf_DVIChecksum != f->f_checksum)
	GripeDifferentChecksums(fname, p->paf_DVIChecksum,
				f->f_checksum);
    
    fi->pspace = p->paf_DVIMag / 6;	/* a three-unit "thin space" */
    fi->nspace = -4 * fi->pspace;
    fi->vspace = 5 * fi->pspace;
}

static void
ReadPostAmble()
{
    
    if (FontFinder == 0) {
	if ((FontFinder = SCreate(sizeof(struct fontinfo))) == 0) {
	    fprintf(stderr, "can not create FontFinder (out of memory?)");
	    exit(1);
	}
    }

    ScanPostAmble(dviFile, PostAmbleHeader, PostAmbleFontDef);

    if (FontErrors)
	GripeMissingFontsPreventOutput(FontErrors);
}

/*
 *	Read the preamble and do a few sanity checks
 */

static void
ReadPreAmble()
{
	register int n;

	rewind(dviFile);
	if (GetByte(dviFile) != Sign8(DVI_PRE))
		GripeMissingOp("PRE");

	if (GetByte(dviFile) != Sign8(DVI_VERSION))
		GripeMismatchedValue("version numbers");

	if (GetLong(dviFile) != Numerator)
		GripeMismatchedValue("numerator");

	if (GetLong(dviFile) != Denominator)
		GripeMismatchedValue("denominator");

	if (GetLong(dviFile) != Magnification)
		GripeMismatchedValue("\\magfactor");

	n = UnSign8(GetByte(dviFile));
	while (--n >= 0)
		(void) GetByte(dviFile);
}

int
dviInit()
{
    extern char *ProgName;
    FILE *tmpFile;
    char *mktemp();

    dviFini();	/* clean up the old files */

    if (dviFileName == 0) {
	dviFile = stdin;
	dviFileName = "<stdin>";
    }else if ((dviFile = fopen(dviFileName, "r")) == NULL) {
	int n = strlen(dviFileName);
	char *dvi_name;
	
	if (strcmp(dviFileName + n - sizeof(".dvi") + 1, ".dvi") == 0) {
	    perror(dviFileName);
	    return(1);
	}

	dvi_name = (char *) malloc((unsigned) n + sizeof(".dvi"));
	sprintf(dvi_name, "%s.dvi", dviFileName);

	if ((dviFile = fopen(dvi_name, "r")) == NULL) {
	    perror(dvi_name);
	    return(1);
	}
	dviFileName = dvi_name;
    }

/*
 *	node, copy the file to a temporary location. This lets the person
 *	peruse the file while theyre re-texing it
 */
    strcpy(dviTmpFileNameBuffer,"/tmp/dvistuff.XXXXXX");
    dviTmpFileName = mktemp(dviTmpFileNameBuffer);

    if (!(tmpFile = fopen(dviTmpFileName,"w+"))) {
	perror("fopen");
	fprintf(stderr,"Unable to create temporary file");
	dviTmpFileName = 0;
    } else {
	char buffer[BUFSIZ];
	int b;

	rewind(dviFile);
	do {
	    b = fread(buffer, 1, BUFSIZ, dviFile);
	    fwrite(buffer, 1, b, tmpFile);
	} while (! feof(dviFile));

	fclose(dviFile);
	dviFile = tmpFile;
	rewind(dviFile);
    }
/*
 *	Unlink the temporary file. This keeps tmp files from cluddering
 *	up /tmp and it does it in a very application-independent way.
 *	You can't reopen the tmp file, but we don't really allow that
 *	anyway (the tmp file is hidden from the user).
 */
    if (dviTmpFileName != 0 &&
	strncmp(dviTmpFileName,"/tmp/",5) == 0) {
	    unlink(dviTmpFileName);
	}

    if (dviUserMag == -1) {
	dviUserMag = 1000;
    }

    if (dviMaxDrift == -1) {
	dviMaxDrift = DEFAULT_MAX_DRIFT;
    }

    if (dviDPI == -1) {
	dviDPI = DEFAULT_DPI;
    }

    if (dviBlackness == -1) {
	dviBlackness = DEFAULT_BLACKNESS;
    }

    if (dviFontRotation == -1) {
	dviFontRotation = ROT_NORM;
    }

    ReadPostAmble();
    ReadPreAmble();

    /* Margins -- needs work! */

    if (dviHHMargin == -1) {
	dviHHMargin = DEFAULT_HHMARGIN;
    }

    if (dviVVMargin == -1) {
	dviVVMargin = DEFAULT_VVMARGIN;
    }

    dviCurrentFont = &NoFont; /* fake font, all zeros */

    return(0);
}

void
dviFini()
{
    if (dviFile != 0) {
	fclose(dviFile);
    }

    if (dviTmpFileName != 0 &&
	strncmp(dviTmpFileName,"/tmp/",5) == 0) {
	    unlink(dviTmpFileName);
	}
}
	

static void
SelectFont(n)
i32 n;
{
    int x = S_LOOKUP;

    if ((dviCurrentFont = (struct fontinfo *)SSearch(FontFinder, n, &x)) == 0)
	GripeNoSuchFont(n);
}

/* Read the postamble. */

/*
 * Skip a font definition (since we are using those from the postamble)
 */

/*ARGSUSED*/
void
SkipFontDef(font)
i32 font;
{
    register int i;

    (void) GetLong(dviFile);
    (void) GetLong(dviFile);
    (void) GetLong(dviFile);
    i = UnSign8(GetByte(dviFile)) + UnSign8(GetByte(dviFile));
    while (--i >= 0)
	(void) GetByte(dviFile);
}


static void
doDviChar(c, advance)
int c;
int advance;
{
    register struct glyph *g;
    register struct font *f;
    int p;

    f = dviCurrentFont->f;

    if (c < f -> f_lowch || c > f -> f_highch) {
	fprintf(stderr,"Character out of range: %d\n",
		c);
	return;
    }

    g = GLYPH(f, c);
    if (!GVALID(g)) {
	fprintf(stderr, "there is no character %d in %s",
	      c, f->f_path);
	exit(1);
    } else {

	if (HASRASTER(g)) {	/* workaround for Imagen bug */
	    applicationPutChar(dviHH,dviVV,c);
	}
	
	if (advance) {
	    dviHH += g->g_pixwidth;
	    dvi_h += g->g_tfmwidth;
	    p = fromSP(dvi_h);
	    FIXDRIFT(dviHH, p);
	}
    }
}

/*
 *	The next two routines are used to reset all the glyphs.
 *	the dviResetFont routine is called for each font in the font table.
 *	It de-allocates the rasters & glyphs for the shrunken fonts, leaving
 *	the original glyph data untouched.
 */


static void
dviResetFont(fi, key)
struct fontinfo *fi;
int key;
{
    if (fi != 0) {
	applicationResetFont(fi, fi -> family);
    }
}
	    
void
dviResetAll()
{
    SEnumerate(FontFinder, dviResetFont);
}

/*
 *	Process the DVI commands for page #page. If the page number is
 *	out of range, it is cropped to the end.
 *
 *	Updates dviCurrentPage
 */
void
dviPreparePage(page)
int page;
{
    register int c;
    register i32 p;
    int advance;

    if (page < 0) {
	page = 0;
    }

    if (page >= dviTotalPages) {
	page = dviTotalPages - 1;
    }

    dviCurrentPage = page;

    fseek(dviFile, (long) pageOffset[page], 0);

    dviHH = dviHHMargin;
    dviVV = dviVVMargin;

    dvi_h = toSP(dviHH);
    dvi_v = toSP(dviVV);

    dvi_w = 0;
    dvi_x = 0;
    dvi_y = 0;
    dvi_z = 0;

    dvi_stackp = dvi_stack;
    
    for(;;) {
/*
 * Get the DVI byte, and switch on its parameter length and type.
 * Note that getchar() returns unsigned values.
 */

	if (feof(dviFile)) {
	    fprintf(stderr,"Unexpected end-of-file\n");
	    exit(1);
	}

	c = getc(dviFile);

	if (DVI_IsChar(c)) {
	    advance = 1;
	    doDviChar(c, advance);
	} else {
	
	switch (DVI_OpLen(c)) {
	    
	case DPL_NONE:
	    break;
	    
	case DPL_SGN1:
	    p = getc(dviFile);
	    p = Sign8(p);
	    break;
	    
	case DPL_SGN2:
	    fGetWord(dviFile, p);
	    p = Sign16(p);
	    break;
	    
	case DPL_SGN3:
	    fGet3Byte(dviFile, p);
	    p = Sign24(p);
	    break;
	    
	case DPL_SGN4:
	    fGetLong(dviFile, p);
	    break;
	    
	case DPL_UNS1:
	    p = UnSign8(getc(dviFile));
	    break;
	    
	case DPL_UNS2:
	    fGetWord(dviFile, p);
	    p = UnSign16(p);
	    break;
	    
	case DPL_UNS3:
	    fGet3Byte(dviFile, p);
	    p = UnSign24(p);
	    break;
	    
	default:
	    panic("DVI_OpLen(%d) = %d", c, DVI_OpLen(c));
	    /* NOTREACHED */
	}
	
	switch (DVI_DT(c)) {
	    
	case DT_SET:
	    c = p;
	    doDviChar(c, 1);
	    break;
	    
	case DT_PUT:
	    c = p;
	    doDviChar(c, 0);
	    break;

	case DT_SETRULE:
	case DT_PUTRULE:
	    
	{
	    i32 h, w, rw;
	    
	    fGetLong(dviFile, h);
	    fGetLong(dviFile, rw);
	    
	    h = ConvRule(h);
	    w = ConvRule(rw);
	    
	    applicationSetRule(dviHH, dviVV, h,w);
	    
	    if (DVI_DT(c) == DT_SETRULE)  {
		dviHH += w;
		dvi_h += rw;
		w = fromSP(dvi_h);
		FIXDRIFT(dviHH, w);
	    }
	}
	    break;
	    
	case DT_NOP:
	    break;
	    
	case DT_BOP:
/*
 *	Each beginning of page has 11 4-byte words telling us things
 *	about the page. We ignore them.
 */
	{
	    fseek(dviFile, (long) (11 * 4), 1);
	}

	    break;
	    
	case DT_EOP:
	    return;
	    
	case DT_PUSH:
	    dvi_stackp->stack_hh = dviHH;
	    dvi_stackp->stack_vv = dviVV;
	    dvi_stackp->stack_dvi = dvi_current;
	    dvi_stackp++;
	    break;
	    
	case DT_POP:
	    dvi_stackp--;
	    dviHH = dvi_stackp->stack_hh;
	    dviVV = dvi_stackp->stack_vv;
	    dvi_current = dvi_stackp->stack_dvi;
	    break;
	    
	case DT_W0:	/* there should be a way to make these pretty */
	    p = dvi_w;
	    goto move_right;
	    
	case DT_W:
	    dvi_w = p;
	    goto move_right;
	    
	case DT_X0:
	    p = dvi_x;
	    goto move_right;
	    
	case DT_X:
	    dvi_x = p;
	    goto move_right;
	    
	case DT_RIGHT:
	move_right:
	    dvi_h += p;
/*
 * DVItype tells us that we must round motions in this way:
 * `When the horizontal motion is small, like a kern, hh
 * changes by rounding the kern; but when the motion is
 * large, hh changes by rounding the true position so that
 * accumulated rounding errors disappear. 
 */
	    if (p >= dviCurrentFont->pspace || p <= dviCurrentFont->nspace)
		dviHH = fromSP(dvi_h);
	    else {
		dviHH += fromSP(p);
		p = fromSP(dvi_h);
		FIXDRIFT(dviHH, p);
	    }
	    break;
	    
	case DT_Y0:
	    p = dvi_y;
	    goto move_down;
	    
	case DT_Y:
	    dvi_y = p;
	    goto move_down;
	    
	case DT_Z0:
	    p = dvi_z;
	    goto move_down;
	    
	case DT_Z:
	    dvi_z = p;
	    goto move_down;
	    
	case DT_DOWN:
	move_down:
	    dvi_v += p;
/*
 * `Vertical motion is done similarly, but with the threshold
 * between ``small and ``large increased by a factor of
 * 5.  The idea is to make fractions like $1\over2$ round
 * consistently, but to absorb accumulated rounding errors in
 * the baseline-skip moves. 
 */
	    if (ABS(p) >= dviCurrentFont->vspace)
		dviVV = fromSP(dvi_v);
	    else {
		dviVV += fromSP(p);
		p = fromSP(dvi_v);
		FIXDRIFT(dviVV, p);
	    }
	    break;
	    
	case DT_FNTNUM:
	    SelectFont((i32) (c - DVI_FNTNUM0));
	    break;
	    
	case DT_FNT:
	    SelectFont(p);
	    break;
	    
	case DT_XXX:
	{
	    char specialBuffer [2048];
	    register char *cp;
	    int sweetp = 0;

	    if (p > 2047) {
		sweetp = p - 2047;
		p = 2047;
	    }
	    
	    for (cp = specialBuffer ; p > 0; p--) {
		*cp = getc(dviFile);
		cp++;
	    }
	    *(cp) = 0;

	    while(sweetp > 0) {
		getc(dviFile);
	    }

	    applicationDoSpecial(specialBuffer);
	}
	    break;
	    
	case DT_FNTDEF:
	    SkipFontDef(p);
	    break;
	    
	case DT_PRE:
	    GripeUnexpectedOp("PRE");
	    /* NOTREACHED */
	    
	case DT_POST:
	    GripeUnexpectedOp("POST");
	    /* NOTREACHED */
	    
	case DT_POSTPOST:
	    GripeUnexpectedOp("POSTPOST");
	    /* NOTREACHED */
	    
	case DT_UNDEF:
	    GripeUndefinedOp(c);
	    /* NOTREACHED */
	    
	default:
	    panic("DVI_DT(%d) = %d", c, DVI_DT(c));
	    /* NOTREACHED */
	}
    }
    }
}

/* Transpose bytes in each pair of bytes  (0123 -> 1032) */
/* Reverse bits in a byte  (01234567 -> 76543210) */
/* needed for sun386i */
/* Gerhard Fleischanderl & Wolfgang Nejdl */

#ifdef sun386
static unsigned char pow2[]={1,2,4,8,16,32,64,128};

unsigned char reverseByte(oldByte)
     unsigned char oldByte;
{
  unsigned short i;
  unsigned char newByte=0;

  /*  build newByte with bits mirrored */

  for(i=0;i<8;i++)
     if (oldByte & pow2[i])
        newByte |= pow2[7-i];

  return newByte;
}

#ifdef UNDEF
#define transposeBytesMirrorBits(pairPtr) \
/* B16 *pairPtr; */ \
/* B16 comprises 2 bytes */ \
{ \
  unsigned char c0,c1, *bP = (unsigned char *) pairPtr; \
 \
  /* transpose neighbouring bytes and \
     reverse bits in each byte */ \
 \
  c0 = *bP; \
  c1 = *(bP+1); \
  *bP = ReverseByteTable[c1]; \
  *(bP+1) = ReverseByteTable[c0]; \
}

#else
#define transposeBytesMirrorBits(ptr) {*ptr = ReverseByteTable[ (*ptr)&0xff ];}
#endif /* undef */

#endif
  

/*
 *	The following routine is used to shrink a glyph by some
 *	shrink factor (in the width & height dimension).
 *
 *	These shrunken glyphs are more appropriate for previewers.
 *	Aspect ratios are observed, if we successfully set vp and hp
 *	so that vp/hp equals the printer font aspect ratio divided by the
 *	previewer aspect ratio.
 *
 *	To do this, we simple scan the original raster, counting the
 *	number of pixels within a given area. If the number of on pixels is
 *	at least twice the total number of pixels, we turn the pixel in
 *	the shrunken glyph ON, else OFF.
 *	With aspect ratios not 1/1 we subsample and weight the pixels.
 *
 *	We use a lookup table to hasten the process of counting pixels.
 *
 *	The method used should be independent of byte-order (I think).
 *
 *	You need to define two types. One should be 32-bits long, and
 *	the other 16 bits long.
 */

typedef unsigned char B8;
typedef unsigned short B16;
typedef unsigned long B32;

#define LOOKUP_BYTES	256
#define	LOOKUP_BITS	8
#define LOOKUP_MASK	0xff

static char dviLookUpTable[LOOKUP_BYTES];
static char tableNotInitialized = 1;

#ifdef sun386
static unsigned char ReverseByteTable[LOOKUP_BYTES];
#endif


struct glyph *
dviShrinkGlyph(gly, shrinkH, shrinkW)
DviGlyph *gly;
int shrinkH;
int shrinkW;
{
    int shrunkHeight;
    int shrunkWidth;
    int glyphWide;
    int glyphHigh;
    int glyphAdvance;
    int shrunkAdvance;

    int bytesWide;
    int shrunkBytesWide;

    struct glyph *ngly;

    B16 *shrunkRaster;
    int rasterSize;

    int x,y;
    int vp, hp, yhp, nextyhp, yend;
    B8 *cp;
    B8 *ptr;

    B32 shrinkMask;

    if (gly == 0 || !HASRASTER(gly)) {
	return(0);
    }

    if (gly -> g_raster == 0) {
	gly-> g_raster = RASTER(gly, dviCurrentFont -> f, dviFontRotation);
    }

/*
 *	Initialize the lookup table of the number of bits in a given byte
 */

    if (tableNotInitialized) {
	register int i;
	register int j;
	register int k;
	register int acc;
	for (i = 0; i < LOOKUP_BYTES; i++) {
	    j = i;
	    acc = 0;
	    for (k = 0; j != 0 && k < LOOKUP_BITS ; k++) {
		acc += (j & 1);
		j >>= 1;
	    }
	    dviLookUpTable[i] = acc;
	}

#ifdef sun386
        for (i = 0; i < LOOKUP_BYTES; i++) {
          ReverseByteTable[i] = reverseByte(i);
        }
#endif

	tableNotInitialized = 0;
    }

    /* get aspect ratio vp/hp of font, check whether it is 1/1 */
    vp = dviCurrentFont->f->f_vppp;
    hp = dviCurrentFont->f->f_hppp;
    if( vp == hp)
	vp = hp = 1;	/* usual case.  could divide both by their GCD */

/*
 *	When shrinking the fonts, we sometimes convert them from byte aligned
 *	to 16 bit-aligned fonts. I think that this is needed by X,
 *	and also SunWindows.
 *	Linux MGR wants 8-bit alignment, but the old SPARC MGR wants 16-bit.
 *	This is done here rather than changing the
 *	library routines because some device drivers, i.e. imagen1,
 *	depend on the byte-aligned unshrunk fonts.
 */
    glyphHigh = gly -> g_height;
    glyphWide = gly -> g_width;

    gly -> g_pixwidth = fromSP(gly -> g_tfmwidth);
    glyphAdvance = gly -> g_pixwidth;

    shrunkHeight = (glyphHigh*hp + shrinkH*vp - 1) / (shrinkH*vp);
    shrunkWidth =  (glyphWide + shrinkW - 1) / shrinkW;
    shrunkAdvance =  (glyphAdvance + shrinkW - 1) / shrinkW;

    bytesWide = (glyphWide + 7) >> 3;

#ifndef OLDMGR
    shrunkBytesWide = (shrunkWidth + 7) >> 3;		/* 8-bit align */
#else
    shrunkBytesWide = ((shrunkWidth + 15) >> 4) * 2;	/* 16-bit align */
#endif

    rasterSize = shrunkHeight * ((shrunkBytesWide+1)&~1);
    shrunkRaster = (B16 *) malloc(rasterSize);
    if (shrunkRaster == NULL) {
	fprintf(stderr, "dviShrinkGlyph out of memory!\n");
	exit(1);
    }
    bzero(shrunkRaster, rasterSize);
    ptr = (B8 *) shrunkRaster;

    for (yhp = 0; yhp < glyphHigh*hp; yhp = nextyhp) {
	y = yhp / hp;
/*
 *	Set the upper limit on the height iteration with yend so we dont count
 *	off the bottom of the raster.
 *	We scale y by hp to subsample pixels if needed.
 */
	nextyhp = yhp + shrinkH*vp;
	if (nextyhp > glyphHigh*hp)
	    nextyhp = glyphHigh*hp;
	yend = (nextyhp - 1) / hp;

	cp = (B8 *) ptr;
	shrinkMask = 0x80;

	for (x = 0; x < glyphWide; x += shrinkW) {
	    int i;
	    int samples, sampleSize, subsamples;

	    B8 *baseP;
	    register int thisShrinkW;

	    baseP = (B8 *) gly -> g_raster + (y * bytesWide);

	    thisShrinkW = x + shrinkW > glyphWide? glyphWide - x: shrinkW;

	    samples = 0;
	    sampleSize = shrinkH * vp * thisShrinkW;

	    for (i = y; i <= yend; i++) {
		register int acc;
		register B8 *p;
		register B8 *ep;
		int wt;
/*
 *	Determine how many subsampled pixel rows are being
 *	counted within this row of pixels.
 */
		if( y == yend)
		    wt = nextyhp - yhp;
		else if( i == y)
		    wt = (y + 1) * hp - yhp;
		else if( i == yend)
		    wt = nextyhp - yend * hp;
		else
		    wt = hp;
/*
 *	Determine how many bytes our shrink window crosses (we might
 *	overlap on byte-edges)
 */

		p = baseP + (x >> 3);
		ep = baseP + ( (x + thisShrinkW - 1) >> 3);
		baseP += bytesWide;

/*
 *	stuff everything in the accumulator
 */

		acc = 0;
		while (p <= ep) {
		    acc = (acc << 8) | *p;
		    p++;
		}

/*
 *	clean off the right hand-side of extra bits, then clean off
 *	the left hand side of extra bits, and then count them.
 */

		subsamples = 0;
		acc = acc >> ( 7 - ((x + thisShrinkW - 1) & 0x7));
		acc &= ~((~0) << thisShrinkW);
		while (acc != 0) {
		    subsamples += dviLookUpTable[ acc & LOOKUP_MASK ];
		    acc >>= LOOKUP_BITS;
		}
		samples += subsamples * wt;
	    }
/*
 *	If at least 1/blackness of the bits are on, treat this entire sample as
 *	being on.
 */

	    if ((samples * dviBlackness) >= sampleSize)
		*ptr |= shrinkMask;
	    shrinkMask >>= 1;
	    if (shrinkMask == 0) {
		shrinkMask = 0x80;
#ifdef sun386
	    transposeBytesMirrorBits(ptr)
#endif
		ptr ++;
	    }
	}
#ifdef sun386
	transposeBytesMirrorBits(ptr)
#endif
	ptr = (B8 *) (cp + shrunkBytesWide);
    }

/*
 *	Build a new glyph from the shrunken raster
 */

#ifdef	UNDEF
    if ( shrunkBytesWide )
      {
        printf("Old glyph:\r\n");
        seeGlyph(gly -> g_raster, glyphHigh, bytesWide);
        printf("%d ", shrunkBytesWide);
        printf("New glyph:\r\n");
        seeGlyph(shrunkRaster, shrunkHeight, shrunkBytesWide);
	sleep(15);
      }
#endif	UNDEF

    ngly = (struct glyph *) malloc(sizeof(struct glyph));
    bzero(ngly, sizeof(struct glyph));

    ngly -> g_raster = (char * ) shrunkRaster;
    ngly -> g_width = shrunkWidth;
    ngly -> g_pixwidth = shrunkAdvance;
    ngly -> g_height = shrunkHeight;

    ngly -> g_xorigin = gly -> g_xorigin / shrinkW;
    ngly -> g_yorigin = gly -> g_yorigin * hp / vp / shrinkH;

    ngly -> g_flags |= GF_SHRUNK;	/* yes, its been shrunk */

    return(ngly);
}

#ifdef	UNDEF

seeGlyph(c, h, w)
char *c;
int h;
int w;
{
    int i,j;

    for (i = 0; i < h; i++ ) {
	for (j = 0; j < w; j++) {
	    int k;
	    register int ch;
	    register int m;
	    char str[9];

	    ch = *(c++);
	    m = 0x80;
	    for (k = 0; k < 8; k++) {
		str[k] = '"' + ( (ch & m) ? 1 : 0 );
		m >>= 1;
	    }
	    str[8] = 0;
	    printf("%s", str);
	}
	printf("\r\n");
    }
}
		
		
#endif	UNDEF
