/* 
______________________________________________________________________
*
* Copyright (c) 1996-2004 QUEST SOFTWARE INC.  All Rights Reserved.
* http://java.quest.com
*
* This software is the confidential and proprietary information of
* Quest Software Inc. ("Confidential Information").  You shall not disclose
* such Confidential Information and shall use it only in accordance with the
* terms of the license agreement you entered into with Quest Software.
*
* QUEST SOFTWARE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
* OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. QUEST SOFTWARE SHALL NOT BE LIABLE FOR ANY
* DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
* ______________________________________________________________________
*/

// RCSID -- $RCSfile: TFMParser.java,v $ $Revision: 1.2 $ $Date: 2006-03-17 21:13:25 $ $Locker:  $  Quest Software Inc.

package com.klg.jclass.page.pcl;

import java.awt.Font;
import java.io.InputStream;
import java.io.IOException;
import com.klg.jclass.page.FontParser;




import java.util.Vector;


/**
 * Parse a tfm file and create a FontPCL for each typeface contained
 * in the file. Typically there is only one defined per file, but there
 * can be more.
 */

public class TFMParser implements FontParser {

/**************************************************************************/
/*                    Data type definitions                               */
/**************************************************************************/

public static final int tagBYTE			= 1;
public static final int	tagASCII		= 2;
public static final int	tagSHORT		= 3;
public static final int	tagLONG			= 4;
public static final int	tagRATIONAL		= 5;
public static final int	tagSIGNEDBYTE	= 16;
public static final int	tagSIGNEDSHORT	= 17;
public static final int	tagSIGNEDLONG	= 18;

/**************************************************************************/
/*                            tag flags                                   */
/**************************************************************************/

protected boolean Copyright_Flag		= false;
protected boolean Comment_Flag			= false;
protected boolean SymbolMap_Flag		= false;
protected boolean SymbolSetDir_Flag		= false;
protected boolean UniqueAssocID_Flag	= false;
protected boolean Point_Flag			= false;
protected boolean NominalPoint_Flag		= false;
protected boolean DesignUnits_Flag		= false;
protected boolean TypeStruct_Flag		= false;
protected boolean StrokeWt_Flag			= false;
protected boolean Spacing_Flag			= false;
protected boolean Slant_Flag			= false;
protected boolean AppearWidth_Flag		= false;
protected boolean SerifStyle_Flag		= false;
protected boolean TypeStyle_Flag		= false;
protected boolean Typeface_Flag			= false;
protected boolean TFSource_Flag			= false;
protected boolean AverageWd_Flag		= false;
protected boolean MaxWidth_Flag			= false;
protected boolean InterWordSP_Flag		= false;
protected boolean ReclineSP_Flag		= false;
protected boolean CapHeight_Flag		= false;
protected boolean XHeight_Flag			= false;
protected boolean Ascent_Flag			= false;
protected boolean Descent_Flag			= false;
protected boolean LowerAscent_Flag		= false;
protected boolean LowerDescent_Flag		= false;
protected boolean UnderDepth_Flag		= false;
protected boolean UnderThick_Flag		= false;
protected boolean UpperAccent_Flag		= false;
protected boolean LowerAccent_Flag		= false;
protected boolean HorizontalEsc_Flag	= false;
protected boolean VerticalEsc_Flag		= false;
protected boolean LeftExtent_Flag		= false;
protected boolean RightExtent_Flag		= false;
protected boolean CharAscent_Flag		= false;
protected boolean CharDescent_Flag		= false;
protected boolean KernPairs_Flag		= false;
protected boolean SectorKern_Flag		= false;
protected boolean TrackKern_Flag		= false;
protected boolean TFSelectStr_Flag		= false;
protected boolean Panose_Flag			= false;

/*
 * constants used in the getit routines
 */
protected static final int TWO_8		= 256;
protected static final int TWO_16		= 65536;
protected static final int TWO_24		= 16777216;


// some globals
static boolean Intel_byte_order;
Buffer	buffer;
public int bufferSize = 5000;
/* This array holds the size (in bytes) of each data type */
short type_size[] = new short[19];

public Vector parse(InputStream is) throws IOException {

	int		i, j;				/* counter variables */

	short	ptype,				/* processor type (Intel vs Motorola) */
			tag_value,			/* numerical tag value */
			data_type,			/* data type -- char, short, long ... */
			number_tags,		/* number of tags per typeface */
			numberTypefaces;	/* number of fonts defined in this file */

	short	num_symsets = 0;

	long	block_size,		/* size of data for a tag (in its data type) */
			length,			/* size of the data for a tag (in bytes) */
			offset,			/* offset position or data area for a tag */
			dir_offset;		/* offset to first directory */

	long	pos = 0;		/* temporary position holder */
	char 	processorFamily;
	String	version;
    Vector	fonts = new Vector(1); /* typically only one font per file */

	// buffer the data locally
	buffer = new Buffer(is);

	/* define the size (in bytes) of each data type */
	type_size[tagBYTE]       = 1;
	type_size[tagASCII]      = 1;
	type_size[tagSHORT]      = 2;
	type_size[tagLONG]       = 4;   /* byte sizes */
	type_size[tagRATIONAL]   = 8;
	type_size[tagSIGNEDBYTE] = 1;
	type_size[tagSIGNEDSHORT]= 2;
	type_size[tagSIGNEDLONG] = 4;

	TypeFacePCL	typeface;

	ptype = getShort();          /* get byte order flag */

	if (ptype == 0X4949) {
		Intel_byte_order = true;		/* set byte order to Intel */
		processorFamily = 'I';
	}
	else if (ptype ==0X4D4D) {
		Intel_byte_order = false;		/* set byte order to Motorola */
		processorFamily = 'M';
	}
	else {
		// throw an exception
	}

	/* read TFM version number */
	version = getVersion(is);

	/* get number of typefaces in the TFM file */
	numberTypefaces = getNumberTypefaces();

	for (i = 0; i < numberTypefaces; i++) {
		typeface = new TypeFacePCL();

		/* get directory offset and goto offset position */
		dir_offset = getLong();
		setPos(dir_offset, 0);

		/* get number of tags */
		number_tags = getShort();

		/* read the specific tag information */
		for (j = 0; j < number_tags; j++) {
			/* read tag information */
			tag_value 	= getShort();
			data_type 	= getShort();
			block_size 	= getLong();

			/* calculate # of bytes for this tag */
			length = (type_size[data_type] * block_size);

			/* if the length is greater than 4 bytes then the data is offset */
			if (length > 4) {
				offset	= getLong();/* read offset */
				pos 	= getPos();		/* save position */
				setPos(offset, 0);	/* seek to offset position */
			}
			else {
				offset = 0L;                  /* data is in offset region */
			}

 /*-----------------------------------------------------------------------*/

			switch(tag_value) {

			/* this tag must be present to ensure a valid TFM file */
			case TagKeys.tagSUBFILE:
				typeface.general.TFMType = (short) getLong();
				break;
			case TagKeys.tagCOPYRIGHT:
				Copyright_Flag = true;     /* indicate that the tag exists */
				/* read copyright string */
				typeface.general.copyright = getString();
				skipBytesInOffsetField(block_size, length);
				break;
			case TagKeys.tagCOMMENT:
				Comment_Flag = true;    /* indicate that the tag exists */
				/* read comment string */
				typeface.general.comment = getString();
				skipBytesInOffsetField(block_size, length);
				break;
			/*
			 * this tag must exist because it provides the number of characters
			 */
			case TagKeys.tagSYMBOLMAP:
				SymbolMap_Flag = true;     /* indicate that the tag exists */
				/* Read the symbol map and create the character table */
				readSymbolMap(typeface, block_size);
				break;
			case TagKeys.tagSYMBOLSETDIR:
				SymbolSetDir_Flag = true;     /* indicate that tag exists */
				/* calculate the number of symbol sets in the typeface */
				num_symsets = (short) (block_size/14);
				/* read in the symbol sets */
				readSymbolSets(typeface, num_symsets);
				break;
			case TagKeys.tagUNIQUEASSOCID:
				UniqueAssocID_Flag = true;    /* indicate that tag exists */
				/* read in unique association identification */
				typeface.general.uniqueAssociationID =getString();
				skipBytesInOffsetField(block_size, length);
				break;
			case TagKeys.tagPOINT:
				Point_Flag = true;      /* indicate that tag exists */
				/* read in point size */
				typeface.typefaceMetrics.point = getRational();
				break;
			case TagKeys.tagNOMINALPOINT:
				NominalPoint_Flag = true;     /* indicate that tag exists */
				/* read in nominal point size */
				typeface.typefaceMetrics.nominalPointSize = getRational();
				break;
			case TagKeys.tagDESIGNUNITS:
				DesignUnits_Flag = true;      /* indicate that tag exists */
				/* read in design units */
				typeface.typefaceMetrics.designUnits = getRational();
				break;
			case TagKeys.tagTYPESTRUCT:
				TypeStruct_Flag = true;      /* indicate that tag exists */
				/* read in stroke weight */
				typeface.typefaceMetrics.typeStruct = getByte();
				skipUnusedBytes(3);
				break;
			case TagKeys.tagSTROKEWT:
            	StrokeWt_Flag = true;      /* indicate that tag exists */
            	/* read in stroke weight */
				typeface.typefaceMetrics.strokeWeight = getByte();
				skipUnusedBytes(3);
				break;
			case TagKeys.tagSPACING:
				Spacing_Flag = true;    /* indicate that tag exists */
				/* read in spacing index */
				typeface.typefaceMetrics.spacing = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagSLANT:
				Slant_Flag = true;      /* indicate that tag exists */
				/* read in slant value */
				typeface.typefaceMetrics.slant = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagAPPEARWIDTH:
				AppearWidth_Flag = true;      /* indicate that tag exists */
				/* read in appearance width */
				typeface.typefaceMetrics.appearanceWidth = getByte();
				skipUnusedBytes(3);
				break;
			case TagKeys.tagSERIFSTYLE:
				SerifStyle_Flag = true;    /* indicate that tag exists */
				/* read in serif style */
				typeface.typefaceMetrics.serifStyle = getByte();
				skipUnusedBytes(3);
				break;
			case TagKeys.tagTYPESTYLE:
				TypeStyle_Flag = true;  /* indicate that tag exists */
				/* read in type style */
				typeface.typefaceMetrics.typeStyle = getByte();
				skipUnusedBytes(3);
				break;
			case TagKeys.tagTYPEFACE:
				Typeface_Flag = true;   /* indicate that tag exists */
				/* read typeface name */
				typeface.general.typeface = getString();
				skipBytesInOffsetField(block_size, length);
				break;
			case TagKeys.tagTFSOURCE:
				TFSource_Flag = true;      /* indicate that tag exists */
				/* read typeface source string */
				typeface.general.typefaceSource = getString();
				skipBytesInOffsetField(block_size, length);
				break;
			case TagKeys.tagAVERAGEWD:
				AverageWd_Flag = true;     /* indicate that tag exists */
				/* read average width */
				typeface.typefaceMetrics.averageWidth = getRational();
				break;
			case TagKeys.tagMAXWIDTH:
				MaxWidth_Flag = true;      /* indicate that tag exists */
				/* read maximum width */
				typeface.typefaceMetrics.maximumWidth = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagINTERWORDSP:
				InterWordSP_Flag = true;      /* indicate that tag exists */
				/* read inter-word spacing */
				typeface.typefaceMetrics.interWordSpacing = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagRECLINESP:
				ReclineSP_Flag = true;     /* indicate that tag exists */
				/* read recommended line spacing */
				typeface.typefaceMetrics.recommendedLineSpacing = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagCAPHEIGHT:
				CapHeight_Flag = true;     /* indicate that tag exists */
				/* read capheight value*/
				typeface.typefaceMetrics.capHeight = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagXHEIGHT:
				XHeight_Flag = true;    /* indicate that tag exists */
				/* read X height value */
				typeface.typefaceMetrics.xHeight = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagASCENT:
				Ascent_Flag = true;     /* indicate that tag exists */
				/* read ascent value */
				typeface.typefaceMetrics.ascent = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagDESCENT:
				Descent_Flag = true;    /* indicate that tag exists */
				/* read descent value */
				typeface.typefaceMetrics.descent = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagLOWERASCENT:
				LowerAscent_Flag = true;      /* indicate that tag exists */
				/* read lowercase ascent */
				typeface.typefaceMetrics.lowercaseAscent = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagLOWERDESCENT:
				LowerDescent_Flag = true;     /* indicate that tag exists */
				/* read lowercase descent */
				typeface.typefaceMetrics.lowercaseDescent = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagUNDERDEPTH:
            	UnderDepth_Flag = true;    /* indicate that tag exists */
				/* read underscore descent */
				typeface.typefaceMetrics.underscoreDescent = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagUNDERTHICK:
				UnderThick_Flag = true;    /* indicate that tag exists */
				/* read underscore thickness */
				typeface.typefaceMetrics.underscoreThickness = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagUPPERACCENT:
				UpperAccent_Flag = true;      /* indicate that tag exists */
				/* read uppercase accent height */
				typeface.typefaceMetrics.uppercaseAccentHeight = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagLOWERACCENT:
				LowerAccent_Flag = true;      /* indicate that tag exists */
				/* read lowercase accent height */
				typeface.typefaceMetrics.lowercaseAccentHeight = getShort();
				skipUnusedBytes(2);
				break;
			case TagKeys.tagHORIZONTALESC:
				HorizontalEsc_Flag = true;    /* indicate that tag exists */
				/* read horizontal escapement for each character */
				readHorizontalEscapement(typeface);
				break;
			case TagKeys.tagVERTICALESC:
				VerticalEsc_Flag = true;      /* indicate that tag exists */
				/* read vertical escapement for each character */
				readVerticalEscapement(typeface);
				break;
			case TagKeys.tagLEFTEXTENT:
				LeftExtent_Flag = true;    /* indicate that tag exists */
				/* read left extent for each character */
				readLeftExtent(typeface);
				break;
			case TagKeys.tagRIGHTEXTENT:
				RightExtent_Flag = true;      /* indicate that tag exists */

				/* read right extent for each character */
				readRightExtent(typeface);
				break;
			case TagKeys.tagCHARASCENT:
				CharAscent_Flag = true;    /* indicate that tag exists */
				/* read character ascent for each character */
				readCharAscent(typeface);
				break;
			case TagKeys.tagCHARDESCENT:
				CharDescent_Flag = true;      /* indicate that tag exists */
				/* read chararacter descent for each character */
				readCharDescent(typeface);
				break;
			case TagKeys.tagKERNPAIRS:
				KernPairs_Flag = true;     /* indicate that tag exists */
				/* Read the kern pairs */
				readKernPairs(typeface);
				break;
			case TagKeys.tagSECTORKERN:
				SectorKern_Flag = true;    /* indicate that tag exists */
				/* Read the sector kern data */
				readSectorKern(typeface);
				break;
			case TagKeys.tagTRACKKERN:
				TrackKern_Flag = true;     /* indicate that tag exists */
				/* Read the track kern data */
				readTrackKern(typeface);
				break;
			case TagKeys.tagTFSELECTSTR:
				TFSelectStr_Flag = true;      /* indicate that tag exists */
				/* read typeface selection string */
				typeface.general.typefaceSelectionString = getString();
				skipBytesInOffsetField(block_size, length);
				break;
			case TagKeys.tagPANOSE:
				Panose_Flag = true;
				typeface.panose.familyType	= getByte();
				typeface.panose.serifStyle	= getByte();
				typeface.panose.weight		= getByte();
				typeface.panose.proportion	= getByte();
				typeface.panose.contrast	= getByte();
				typeface.panose.strokeVariation = getByte();
				typeface.panose.armStyle	= getByte();
				typeface.panose.letterForm	= getByte();
				typeface.panose.midLine		= getByte();
				typeface.panose.xHeight		= getByte();
				break;
			/* used when the tag is not found within the case statements */
			default:
				/* advance position in TFM file */
				getLong();
				break;
			}	/* end switch */

			/* if the data was offset, reset the position */
			if (offset !=0L) {
				setPos(pos, 0);
			}

		}	/* end for tags */

		/* Build the character table (list) for an ISO-Latin-1 font */
		buildCharacterTable(typeface);

		int size = -1; // it's a scaled font
		String name	= typeface.getName();
		int style	= typeface.getStyle();

		// Spacing indicates a standard (fixed) spacing for characters
		if (Spacing_Flag && (int) typeface.typefaceMetrics.spacing != 0) {

			// Adjust the style setting of the typeface
			style |= TypeFacePCL.PCL_TYPEFACE_FLAGS_FIXEDWIDTH;
			typeface.setStyle(style);
		}

		// add this font to the temporary list of fonts to be returned
		FontPCL font = new FontPCL(name, style, size, typeface);
		fonts.addElement(font);

	}  /* end for number of typefaces */

	return fonts;

} /* end parse */

public double getRational() {
    float numerator,denominator;	/* temporary variables to store the longs */

    numerator 	= getLong();   	/* read the numerator   */
    denominator = getLong();	/* read the denominator */

    return(numerator / denominator);/* return the result of the division */
}


public long getPos() {
	return(buffer.offset);
}

public int setPos(long offset, int relative) {
	long new_offset;

	if (relative == 0) {
		new_offset = offset;
	}
	else if (relative == 1) {
		new_offset = buffer.offset + offset;
	}
	else {
		new_offset = buffer.max_offset - offset;
	}

	/* Check for an invalid seek operation */
	if (new_offset < 0 || new_offset > buffer.max_offset) {
		return(-1);
	}

	/* Otherwise set the new position */
	buffer.offset = (int) new_offset;
	return(0);
}



/**
 * Based on the original by Doug Kaltenecker, 8/4/89.
 * Reads the version number of the TFM file and checks
 * to see if the high bit is set.  If it is, it appends
 * a leading minus sign on the version number.
 */
public String getVersion(InputStream is) {
	int		majorv,minorv;
	short	min, submin;
	short	type;
	String 	version;

	/* get version numbers */
	majorv = getByte();
	minorv = getByte();

	min 	= (short) ((minorv & 240) >> 4);
	submin 	= (short) (minorv & 15);

	type = (short) (majorv & 128);

	if (type <= 0) {
		version = majorv + "." + min + "." + submin;
	}
	else {
		majorv = (byte) (majorv & 127);
		version = "-" + majorv + "." + min + "." + submin;
	}

	return version;
}

/**
 * Based on the original by Doug Kaltenecker, 8/4/89
 * Reads through the the TFM file in order to determine the
 * number of typefaces contained in the file.
 */
public short getNumberTypefaces() {
	long	dir_offset,					/* offset to first directory */
			typeface_offset_location,	/* offset to additional directories */
			next_typeface;				/* value at the offset region */

	short	num_tags;					/* number of tag entries in typeface */
	long	first_dir;					/* temporary position variable */
	boolean	done = false;				/* end of file Flag */
	int		Num_typefaces = 1;

	first_dir = getPos();			/* save current position */
	dir_offset = getLong();			/* get first directory offset */
	setPos(dir_offset, 0);			/* seek to offset position */

	/* while there are additional directories */
	while (!done) {
		/* get the number of tag entries in the typeface */
		num_tags = getShort();

		/* calculate the directory offset location from begining of the file */
		typeface_offset_location = ((num_tags * 12) + dir_offset);

		/* seek to the offset location */
		setPos(typeface_offset_location, 0);
		next_typeface = getLong();	/* get next typeface offset */

		if (next_typeface != 0L) { 		/* if the offset does not equal zero */
			setPos(next_typeface, 0);	/* seek to the offset position */
			Num_typefaces++;    		/* increment the number of typefaces */
		}

		/* if offset is equal to zero, there are no more typefaces */
		else
			done = true;
	}
	setPos(first_dir, 0);	/* reset the position */

    return (short) Num_typefaces;
}

public void skipBytesInOffsetField(long block_size, long length) {
	/*
	 * if the length is less than or equal to 4, the data is
	 * not offset, therefore after the data is read, bytes
	 * must be skipped
	 */
	if (length <= 4) {
		for (int i = (int) block_size; i < 4; i++)
			getByte();
	}
}

public void readSymbolMap(TypeFacePCL typeface, long number_chars) {
	int						 i, length;
	CharacterPCL character;

	/* set total number of characters in typeface */
	typeface.general.numberCharacters = number_chars;

	/*
	 * allocate memory for the symbol set map;
	 * the length is the total number of bytes
	 * (typesize[data_type] * number_chars)
	 */
	// length = (int) (type_size[tagSHORT] * number_chars);
	typeface.symbol.symbolMap = new short[(int)number_chars];

	/* read in MSL number for every character */
	for (i = 0; i < number_chars; i++) {

		/* create a character's entry for the character list */
		character = new CharacterPCL();

		/* Read the MSL index number */
		character.MSLIndex = getShort();
		typeface.symbol.symbolMap[i] = character.MSLIndex;

		/* set the index for the character */
		character.index = i;

		/* Add the character to the character list */
		typeface.charMetrics.chars.addElement(character);

	}
}

public void readSymbolSets(TypeFacePCL typeface, int num_symsets) {
	int									 i;
	// KlgPfObject			list;
	SymbolSetDirectoryPCL	symbol_set;

	/* assign the number of symbol sets to the structure variable */
	typeface.general.numberSymbolSets = num_symsets;

	/* read in data for each symbol set */
	for (i = 0; i < num_symsets; i++) {

		/* Read the symbol set directory and add it to the list */
		symbol_set = readSymbolSetDirectory();
		typeface.symbol.symbolSets.addElement(symbol_set);
	}
}

public SymbolSetDirectoryPCL readSymbolSetDirectory() {
	int						i = 0;
	short					index[];
	long					nextl, pos;
	long					index_offset, index_length;
	SymbolSetDirectoryPCL	symbol_set;

	symbol_set = new SymbolSetDirectoryPCL();

	nextl = getLong();		/* read offset to symbol set name */
	pos = getPos();			/* save position */

	setPos(nextl, 0);		/* seek to offset location */

	/* read symbol set name */
	symbol_set.symbolName = getString();

	setPos(pos, 0);			/* restore position */

	nextl = getLong();		/* get offset to selection string */
	pos = getPos();			/* save position */

	setPos(nextl, 0);		/* seek to offset location */

	/* read selection string */
	symbol_set.selectionName = getString();

	setPos(pos, 0);			/* restore position */

	/* read offset and length of index array */
	index_offset = getLong();
	index_length = getShort();

	pos = getPos();			/* save position */

	/* assign symbol set index array length to structure variable */
	symbol_set.symbolLength = (short) index_length;

	/* allocate memory to tempory pointer for symbol set index array */
	index = new short[(int) index_length ];

	setPos(index_offset, 0);

	/* read symbol indicies */
	for (i = 0; i < index_length; i++) {
		index[i] = getShort();
	}

	/* assign pointer to structure variable */
	symbol_set.symbolIndex = index;

	setPos(pos, 0);			/* restore position */

	return(symbol_set);
}

public void readHorizontalEscapement(TypeFacePCL typeface) {
	/* read in the horizontal escapement for every character */
	for (int i = 0; i < typeface.general.numberCharacters; i++) {

		/* get the character's entry from the character list */
		CharacterPCL character = (CharacterPCL) typeface.getCharacter(i);

		/* set the horizontal escapement for the character */
		if (character != null) {
			character.horizontalEscapement = getShort();
		}
	}
}

public void readVerticalEscapement(TypeFacePCL typeface) {
	/* read in the vertical escapement for every character */
	for (int i = 0; i < typeface.general.numberCharacters; i++) {

		/* get the character's entry from the character list */
		CharacterPCL character = (CharacterPCL) typeface.getCharacter(i);

		/* set the vertical escapement for the character */
		if (character != null) {
			character.verticalEscapement = getShort();
		}
	}
}

public void readLeftExtent(TypeFacePCL typeface) {
	/* read in the left extent for every character */
	for (int i = 0; i < typeface.general.numberCharacters; i++) {

		/* get the character's entry from the character list */
		CharacterPCL character = (CharacterPCL) typeface.getCharacter(i);

		/* set the left extent for the character */
		if (character != null) {
			character.leftExtent = getShort();
		}
	}
}

public void readRightExtent(TypeFacePCL typeface) {
	/* read in the right extent for every character */
	for (int i = 0; i < typeface.general.numberCharacters; i++) {

		/* get the character's entry from the character list */
		CharacterPCL character = (CharacterPCL) typeface.getCharacter(i);

		/* set the right extent for the character */
		if (character != null) {
			character.rightExtent = getShort();
		}
	}
}

public void readCharAscent(TypeFacePCL typeface) {
	/* read in the per-char ascent for every character */
	for (int i = 0; i < typeface.general.numberCharacters; i++) {

		/* get the character's entry from the character list */
		CharacterPCL character = (CharacterPCL) typeface.getCharacter(i);

		/* set the per-char ascent for the character */
		if (character != null) {
			character.characterAscent = getShort();
		}
	}
}

public void readCharDescent(TypeFacePCL typeface) {
	/* read in the per-char descent for every character */
	for (int i = 0; i < typeface.general.numberCharacters; i++) {

		/* get the character's entry from the character list */
		CharacterPCL character = (CharacterPCL) typeface.getCharacter(i);

		/* set the per-char descent for the character */
		if (character != null) {
			character.characterDescent = getShort();
		}
	}
}

public void readKernPairs(TypeFacePCL typeface) {
	/* get number of kern pairs and assign to structure variable */
	short number_pairs = getShort();
	typeface.kerning.numberPairs = number_pairs;

	/* read in pair kerning information for each character */
	for (int i = 0; i < number_pairs; i++) {

		/* Create the kern pair structure */
		KernPairPCL kern_pair = new KernPairPCL();

		/* read first character index */
		kern_pair.firstCharIndex = getShort();
		/* read second character index */
		kern_pair.secondCharIndex = getShort();
		/* read kerning value */
		kern_pair.kernValue = getShort();

		/* Add the kern pair to the list */
		typeface.kerning.kernPairs.addElement(kern_pair);
	}
}

public void readSectorKern(TypeFacePCL typeface) {
	int number_chars, number_sectors;

	/* get number of sector kern characters */
	number_chars = getShort();

	/* assign number of sector kern chars to structure variable */
	typeface.kerning.numberKernChars = number_chars;

	/* get number of sectors per character */
	number_sectors = getShort();

	/* assign number of sectors to structure variable */
	typeface.kerning.numberSectors = number_sectors;

	/* read in sector kerning info for each sector kern character */
	for (int i = 0; i < number_chars; i++) {

		/* Create the sector kerning structure */
		SectorKernPCL sector_kern = new SectorKernPCL();

		/* read character index */
		sector_kern.charIndex = getShort();

		/* allocate memory to a temporary pointer for left sector */
		sector_kern.leftSector = new short[number_sectors];

		/* allocate memory to a temp pointer for right sector */
		sector_kern.rightSector = new short[number_sectors];

		/* read the left sector values for the character */
		for (int j = 0; j < number_sectors; j++) {
			sector_kern.leftSector[j] = getShort();
		}

		/* read the right sector values for the character */
		for (int j = 0; j < number_sectors; j++) {
			sector_kern.rightSector[j] = getShort();
		}

		/* Add the sector kerning to the list */
		typeface.kerning.sectors.addElement(sector_kern);
	}
}

public void readTrackKern(TypeFacePCL typeface) {
	int					number_tracks, i;
	// KlgPfObject		list;
	TrackKernPCL		track_kern;

	/* Create the list for track kerning data
	list = KlgPfListCreate(sizeof(KlgPfPCLTrackKernStruct *));
	typeface.kerning.trackKern_list = list;
	KlgPfListSetDestroyProc(list, track_kern_destroy);
	*/

	/* get the number of defined tracks */
	number_tracks = getShort();

	/* assign the number of track to the structure variable */
	typeface.kerning.numberTracks = number_tracks;

	/* read track kerning information for each defined track */
	for (i = 0; i < number_tracks; i++) {

		/* Create the track kerning structure */
		track_kern = new TrackKernPCL();

		/* read track value */
		track_kern.trackValue = getShort();
		/* read maximum point size */
		track_kern.maxPointSize = getShort();
		/* read minimum point size */
		track_kern.minPointSize = getShort();
		/* read maximum kerning amount */
		track_kern.maxKern = getShort();
		/* read minimum kerning amount */
		track_kern.minKern = getShort();

		/* Add the track kerning to the list */
		// KlgPfListAppend(list, (XtPointer) &track_kern);
	}
}

public void buildCharacterTable(TypeFacePCL typeface) {
	int									 num_pairs;
	// KlgPfObject							 list;
	CharacterPCL			metrics;
	CharacterPCL			exclam;
	// KlgPfPCLKernPairsStruct				*kern_pair;
	SymbolSetDirectoryPCL latin1_set;
	CharSetEntryPCL char_entry = null;
	CharSetEntryPCL space_entry = null;

	/* Sort the character metrics list of characters and sort the list
	KlgPfListSetCompareProc(typeface.charMetrics.char_list,
							klgpf_fontPCL_compareCharIndex);
	KlgPfListSort(typeface.charMetrics.char_list);
    */

	/* find the latin-1 symbol set */
	latin1_set = findSymbolSet(typeface, "11J");
	if (latin1_set == null) {
		latin1_set = findSymbolSet(typeface, "0N");
	}
	if (latin1_set == null) {
		latin1_set = findSymbolSet(typeface, "9U");
	}
	if (latin1_set == null) {
		latin1_set = findSymbolSet(typeface, "19U");
	}

	/*
	 * These are fallback modes to partial character sets:
	 * using these may cause some problems, but at least allows
	 * some use of symbol and other special fonts
	 */
	if (latin1_set == null) {
		/* If we remap the Roman-8 character set it might really work */
		latin1_set = findSymbolSet(typeface, "8U");
	}
	if (latin1_set == null) {
		/* This is ASCII which every text font should support */
		latin1_set = findSymbolSet(typeface, "0U");
	}
	if (latin1_set == null) {
		/*
		 * If we still haven't got a symbol set this probably isn't a
		 * text font, so it doesn't matter much what symbol set we use
         */
		latin1_set = (SymbolSetDirectoryPCL) typeface.symbol.symbolSets.elementAt(0);

	}

	/* Store the selected symbol set */
	typeface.symbol.latin1Set = latin1_set.selectionName;

	/* look up the characters in the symbol set, adding them to the list */
	for (int i = 0; i < latin1_set.symbolLength; i++) {

		/* Don't bother creating an entry if no data exists for this value */
		if (latin1_set.symbolIndex[i] != -1) {
			char_entry = new CharSetEntryPCL();
			char_entry.code = i;
			char_entry.index = latin1_set.symbolIndex[i];
			metrics = (CharacterPCL)
                typeface.charMetrics.chars.elementAt(char_entry.index);
			char_entry.metrics = metrics;

			// add it to the list, indexed by index
            typeface.charMetrics.charSetEntries.put(
                new Short((short) char_entry.index), char_entry);
		}
	}

	/*
	 * loop through the kern pairs, adding any that are used in the symbol
	 * set to the list
	 */
	num_pairs = typeface.kerning.kernPairs.size();

	for (int i = 0; i < num_pairs; i++) {
		KernPairPCL kern_pair = (KernPairPCL) typeface.kerning.kernPairs.elementAt(i);
		/* If the kerning is against a character in the character set */
		char_entry = typeface.charMetrics.findEntryByIndex(kern_pair.secondCharIndex);
		if (char_entry != null) {

			/* If the kerning is also from a character in the set */
			char_entry = typeface.charMetrics.findEntryByIndex(kern_pair.firstCharIndex);

			if (char_entry != null) {
				/* Add the kerning information to the list */
				char_entry.kerns.put(
					new Integer(kern_pair.secondCharIndex), kern_pair);
			}
		}
	}

	/*
	 * The character set list was sorted by MSL index for the purpose
	 * of building the list (looking up kerning characters), so now it
	 * needs to be re-sorted
	KlgPfListSetCompareProc(list, klgpf_fontPCL_charSetCompareAscii);
	KlgPfListSort(list);
	 */

	/*
	 * Check if the space character (0x20) maps to a character or not;
	 * if not, try to find or build a space character
	 */
	space_entry = typeface.charMetrics.findEntryByCode((short) 0x20);
	if (space_entry == null) {

		/* Create a space character and add it to the character set */
		space_entry = new CharSetEntryPCL();
		space_entry.code = 0x20;
		// space_entry.kerns = null;
		// add it to the list
        typeface.charMetrics.charSetEntries.put(
                new Short((short) space_entry.code), space_entry);

		/* Space is usually the zeroth character in the list */
		metrics = typeface.charMetrics.findCharByIndex((short) 0);

		/* Grab the exclamation mark for comparison purposes */
		char_entry = typeface.charMetrics.findEntryByCode((short) 33);
		exclam = typeface.charMetrics.findCharByIndex(char_entry.index);

		/* Make sure the metrics of character zero are "sane" */
		if (metrics == null || metrics.leftExtent != 0 ||
			metrics.characterDescent != 0 ||
			(metrics.characterAscent != 0 &&
			 metrics.characterAscent != exclam.characterAscent) ||
			metrics.horizontalEscapement != exclam.horizontalEscapement)
		{

			/* If not, build a fake space based on the exclamation mark */
			space_entry.metrics = new CharacterPCL();
			space_entry.metrics.MSLIndex = -1;
			space_entry.metrics.index = 0;
			space_entry.metrics.horizontalEscapement =
											exclam.horizontalEscapement;
			space_entry.metrics.verticalEscapement = 0;
			space_entry.metrics.leftExtent = 0;
			space_entry.metrics.rightExtent = (short)
											(exclam.horizontalEscapement / 2);
			space_entry.metrics.characterAscent = exclam.characterAscent;
			space_entry.metrics.characterDescent = 0;

			/* Store the new structure so it won't get leaked */
			typeface.charMetrics.chars.addElement(space_entry.metrics);


			/* Arbitrarily assign an index of 0 */
			space_entry.index = 0;
		}
		else {

			/* If they are sane, use character 0 for the metrics */
			space_entry.metrics = metrics;
			space_entry.index = 0;
		}
	}
}

/**
 */
public SymbolSetDirectoryPCL
		findSymbolSet(TypeFacePCL typeface, String set_name) {
	// Given a set name find its index
	return typeface.symbol.findSymbolSetDirByName(set_name);
}

public void skipUnusedBytes(int bytes) {
	if (bytes >= 2) {
		getShort();
	}

	if (bytes == 1 || bytes == 3) {
		getByte();
	}
}

//////////////////////// IO Methods ///////////////////////////////////////

public int getByte() {
	return (buffer.data[buffer.offset++] & 0xff);	/* return one byte */
}


public short getShort() {
	int b1, b2;		/* temporary variables to store a byte into */

	b1 = buffer.data[buffer.offset++]& 0xff;		/* read the bytes */
	b2 = buffer.data[buffer.offset++]& 0xff;

	if (Intel_byte_order) { /* check for byte order */
		return (short) ((TWO_8 * b2) + b1);
	}
	else {
		return (short) ((TWO_8 * b1) + b2);
	}
}

public long getLong() {
	long b1, b2, b3, b4;    /* temporary variables to store a byte into */

	b1 = buffer.data[buffer.offset++]& 0xff;
	b2 = buffer.data[buffer.offset++]& 0xff;      /* read the bytes */
	b3 = buffer.data[buffer.offset++]& 0xff;
	b4 = buffer.data[buffer.offset++]& 0xff;

	if (Intel_byte_order) {   /* check for byte order */
	    return((TWO_24 * b4) + (TWO_16 * b3) + (TWO_8 * b2) + b1);
	}
	else {
	    return((TWO_24 * b1) + (TWO_16 * b2) + (TWO_8 * b3) + b4);
	}
}

public String getString() {
	int i = 0;

    /* read in characters until a zero is reached */
	byte b;
	java.util.Vector v = new java.util.Vector();
	while ( (b = (byte) buffer.data[buffer.offset++]) != 0) {
    	v.addElement(new Byte(b));
	}
	byte bytes[] = new byte[v.size()];
	for (int j = 0; j < v.size(); j++) {
		bytes[j] = ((Byte)v.elementAt(j)).byteValue();
	}
	return new String(bytes);
}

/**
 * 
 * Buffer size for reading in tfm files.
 * Default is 5000 bytes.
 */
public void setBufferSize(int bufferSize) {
	this.bufferSize = bufferSize;
}

/**
 * 
 * Buffer size for reading in tfm files.
 *
 */
public int getBufferSize() {
	return bufferSize;
}

/**
 * Internal buffer used to read in tfm file.
 */
class Buffer {
	byte data[];
	int	offset 		= 0;
	int	max_offset 	= 0;
	int	size;
    Buffer(InputStream is) throws IOException {
		this.data 	= buildBuffer(new java.io.BufferedInputStream(is));
		max_offset 	= data.length;
	}
	/**
 	*/
	public byte[] buildBuffer(InputStream is) throws IOException {
    	byte buffer[]   = new byte[getBufferSize()]; // temp buffer
    	byte blob[]     = new byte[getBufferSize()]; // start off at chunksize
    	int totalRead = 0, numRead = 0;
    	while( (numRead = is.read(buffer)) > 0 ) {
        	byte oldData[] = blob;
        	blob = new byte[totalRead + numRead];
        	System.arraycopy(oldData, 0, blob, 0, totalRead);
        	System.arraycopy(buffer, 0, blob, totalRead, numRead);
        	totalRead += numRead;
    	}
    	return blob;
	}
}


}
