/*
 *	GifFont -
 *		Draw text using bitmap images
 *
 *			Paul Haeberli - 1995
 */
import java.awt.*;
import java.awt.image.*;
import java.lang.*;
import java.applet.*;
import java.net.*;
import java.awt.image.*;
import java.util.Random;
import java.io.*;

/*
    0 - xadvance
    1 - xsize
    2 - ysize
    3 - xdorg
    4 - ydorg
*/

public class GifFont extends Object {
    int xpos, ypos;
    short fontdata[][];
    int fontpixoff[];
    Image fontglyphs[];
    int advancedivisor;
    int xlineadv, ylineadv;
    int advancecode, nglyphs;
    int startcode;
    double xadv, spacing;
    Image fontgif;
    MemoryImage memimg;
    Applet theapplet;

    GifFont(Applet applet, URL docbase, String fontname) {
	InputStream is = null;
	byte buf[] = new byte[2];
	short val;
	int sizecode, i, n, nb, lval, nshorts;
	String gifname;
	URL gifURL;

	theapplet = applet;
	Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
	try {
	    is = new URL(docbase,fontname).openStream();
	    is.read(buf,0,2);
	    if(buf[0] != 23 && buf[1] != 45) {
		System.out.println("GifFont: bad magic numbers in font file ");
		System.out.println(buf[0]);
		System.out.println(buf[1]);
		return;
	    }
	    is.read(buf,0,2);
	    advancedivisor = (int)((buf[0]<<8)|(buf[1]<<0));
	    is.read(buf,0,2);
	    xlineadv = (int)((buf[0]<<8)|(buf[1]<<0));
	    is.read(buf,0,2);
	    ylineadv = (int)((buf[0]<<8)|(buf[1]<<0));
	    is.read(buf,0,1);
	    advancecode = buf[0];
	    if(advancecode != 4) {
		System.out.println("GifFont: advancecode must be 4");
		return;
	    }
	    is.read(buf,0,2);
	    nglyphs = (int)((buf[0]<<8)|(buf[1]<<0));
	    is.read(buf,0,2);
	    startcode = (int)((buf[0]<<8)|(buf[1]<<0));
	    while(true) {
		is.read(buf,0,2);
		n = (int)((buf[0]<<8)|(buf[1]<<0));
		if(n == 0)
		    break;
		is.read(buf,0,2);
		System.out.println("GifFont: only using first glyphset");
	    }
	    fontdata = new short[nglyphs][5];
	    fontpixoff = new int[nglyphs];
	    fontglyphs = new Image[nglyphs];
	    for(n=0; n<nglyphs; n++) {
		is.read(buf,0,1);
		sizecode = buf[0];

		if((sizecode & (1<<0))!=0) {	/* advance */
		    is.read(buf,0,2);
		    fontdata[n][0] = (short)((buf[0]<<8)|(buf[1]<<0));
		} else {
		    is.read(buf,0,1);
		    fontdata[n][0] = buf[0];
		}

		if((sizecode & (1<<1))!=0) {	/* xsize & ysize */
		    is.read(buf,0,2);
		    fontdata[n][1] = (short)((buf[0]<<8)|(buf[1]<<0));
		    is.read(buf,0,2);
		    fontdata[n][2] = (short)((buf[0]<<8)|(buf[1]<<0));
		} else {
		    is.read(buf,0,1);
		    fontdata[n][1] = buf[0];
		    is.read(buf,0,1);
		    fontdata[n][2] = buf[0];
		}

		nb = 1+((sizecode>>2)&3); 	/* pixoffset */
		lval = 0;
/*
		while((nb--)!=0) {
		    is.read(buf,0,1);
		    lval = (lval<<8)+buf[0];
		}
*/
		while((nb--)!=0) {
                    is.read(buf,0,1);
                    int tval = buf[0];
                    if ( tval < 0 ) {
                       tval += 256;
                    }
                    lval = (lval<<8)+tval;
                }

		fontpixoff[n] = lval;

		if((sizecode & (1<<4))!=0) {	/* xdorg | ydorg */
		    is.read(buf,0,2);
		    fontdata[n][3] = (short)((buf[0]<<8)|(buf[1]<<0));
		    is.read(buf,0,2);
		    fontdata[n][4] = (short)((buf[0]<<8)|(buf[1]<<0));
		} else {
		    is.read(buf,0,1);
		    val = buf[0];
		    if(val>127)
			val = (short)(val-256);
		    fontdata[n][3] = val;
		    is.read(buf,0,1);
		    val = buf[0];
		    if(val>127)
			val = (short)(val-256);
		    fontdata[n][4] = val;
		}
		if((sizecode & (1<<5))!=0) {
		    System.out.println("GifFont: can't read rotated glyphs yet!");
		    return;
		}
		if((sizecode & (1<<6))!=0) {
		    System.out.println("GifFont: can't read multiple imagerects yet!");
		    return;
		}
/*
System.out.print("info for: " + n);
System.out.print(" sizecode: " + sizecode);
System.out.print(" adv: " + fontdata[n][0]);
System.out.print(" size: " + fontdata[n][1]);
System.out.print(" " + fontdata[n][2]);
System.out.print(" pixoff: " + fontpixoff[n]);
System.out.print(" dst: " + fontdata[n][3]);
System.out.println(" " + fontdata[n][4]);
*/
		fontdata[n][4] = (short)(fontdata[n][4]+(fontdata[n][2]-1));
	    }
	} catch (Exception e) {
	    System.out.println("GifFont: Malformed URL");
	}
	try {
            if (is != null)
                is.close();
        } catch(Exception e) {
	    System.out.println("GifFont: Bad Poop 1");
        }
	gifname = fontname + ".gif";
	gifURL = null;
	try {
	    gifURL = new URL(docbase,gifname);
        } catch(Exception e) {
	    System.out.println("GifFont: Bad Poop 2");
        }
	if(gifURL != null) {
	    fontgif = theapplet.getImage(gifURL);
	} else
	    System.out.println("GifFont: Bad Poop 3");
	memimg = new MemoryImage(fontgif);
	if(!memimg.imgready)
	    System.out.println("GifFont: timeout on read of font image!");
	spacing = 0.0;
    }
    public int getSize() {
	return ylineadv;
    }
    public void setSpacing(double npix) {
	spacing = npix;
    }
    public void setPosition(int x, int y) {
	xpos = x;
	ypos = y;
    }
    private void makeglyph(int charno) {
	int xsize, ysize;
	int pixoffset, xorg, yorg;
	int d, s, x, y, pixbuf[], imgpix[];

	xsize = fontdata[charno][1];
	ysize = fontdata[charno][2];
	if(xsize == 0 || ysize == 0)
	    return;
	pixoffset = fontpixoff[charno];
	xorg = pixoffset%memimg.imgxsize;
	yorg = pixoffset/memimg.imgxsize;
	yorg = (memimg.imgysize-1)-yorg-(ysize-1);  /* flip coord sys */
	pixbuf = new int[xsize*ysize];		/* copy hoping for speed */
	imgpix = memimg.imgpixels;
	for(y=0; y<ysize; y++) {
	    d = (ysize-1-y)*xsize;
	    for(x=0; x<xsize; x++)
		pixbuf[d++] = imgpix[pixoffset++];
	}
	fontglyphs[charno] = theapplet.createImage(
			new MemoryImageSource(xsize, ysize, pixbuf, 0, xsize));
	pixbuf = null;
    }
    public void drawChar(Graphics g, int charno) {
	int xoff, yoff;

	charno -= startcode;
	if(charno<0 || charno>=nglyphs)
	    return;
	if(fontglyphs[charno] == null)
	    makeglyph(charno);
	if(fontglyphs[charno] != null) {
	    xoff = fontdata[charno][3];
	    yoff = fontdata[charno][4];
	    g.drawImage(fontglyphs[charno],xpos+((int)xadv)+xoff,ypos-yoff,theapplet);
	}
	xadv += fontdata[charno][0]+spacing;
    }
    public double charWidth(int charno) {
	int nc, np, i, p;

	charno -= startcode;
	if(charno<0 || charno>=nglyphs)
	    return 0.0;
	return fontdata[charno][0];
    }
    public double stringWidth(String str) {
	double width;
	int i, len, c;
	
	width = 0.0;
	len = str.length();
	for(i=0; i<len; i++) 
	    width += charWidth(str.charAt(i));
	return width+spacing*(len-1);
    }
    public void drawCenteredString(Graphics g, String str) {
	int i, len;
	
	xadv = -(int)(stringWidth(str)/2.0);
	len = str.length();
	for(i=0; i<len; i++) 
	    drawChar(g,str.charAt(i));
    }
    public void drawString(Graphics g, String str) {
	int i, len;
	
	xadv = 0;
	len = str.length();
	for(i=0; i<len; i++) 
	    drawChar(g,str.charAt(i));
    }
}
