/* 
______________________________________________________________________
*
* 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: FontMetricsPCL.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.awt.FontMetrics;
import java.awt.Graphics;
import java.text.CharacterIterator;

import com.klg.jclass.page.JCUnit;




import java.awt.font.LineMetrics;


/**
 * FontMetricsPCL allows access to the metrics of a PCL font.
 */



public class FontMetricsPCL extends FontMetrics {


/**
 * Create a new FontMetricPCL
 * @param font the FontPCL for which metric information is to be gotten
 */
public FontMetricsPCL(FontPCL font) {
    super(font);
	if ( !(font instanceof FontPCL) ) {
		throw new java.lang.IllegalArgumentException("font must be instance of FontPCL");
	}
}

/**
 * Overrides FontMetrics method.
 */
public int getAscent() {
	return (int) getAscender(JCUnit.POINTS, font.getSize());
}

/**
 * Overrides FontMetrics method.
 */
public int getDescent() {
	return (int) - getDescender(JCUnit.POINTS, font.getSize());
}

/**
 * Overrides FontMetrics method.
 */
public int getMaxAscent() {
	return (int) getAscender(JCUnit.POINTS, font.getSize());
}

/**
 * Overrides FontMetrics method.
 */
public int getMaxDescent() {
	return (int) getDescender(JCUnit.POINTS, font.getSize());
}

/**
 * Overrides FontMetrics method.
 */
public int charWidth(char ch) {
	return (int) (preciseCharWidth(ch) + 0.5);
}

/**
 * This more precise method is used by both charWidth()
 * and charsWidth() to prevent cumulative round-off error
 * when determining the width of a string.
 */
protected double preciseCharWidth(char ch) {
	CharSetEntryPCL entry = ((FontPCL)font).getCharMetric(ch);
	int width = entry.metrics.getHorizontalEscapement();
	double widthInPoints = convertToDecipoints(width, font.getSize());
	return convertFromDeciPoints(JCUnit.POINTS, widthInPoints);
}

/**
 * Override FontMetrics method.
 */
public int charsWidth(char chars[], int offset, int length) {
    double width = 0.0;
	for (int i = offset; i < length; i++) {
    	width += preciseCharWidth(chars[i]);
	}
	return (int)(width + 0.5);
}

/**
 * Override FontMetrics method.
 */
public LineMetrics getLineMetrics( String string, Graphics context) {
    return (LineMetrics) new LineMetricsPCL(this, string);}

/**
 * Override FontMetrics method.
 */
public LineMetrics getLineMetrics( String string,
                                        int beginIndex, int limit,
                                        Graphics context) {
	// in 1.1, StringBuffer doesn't have a substring() method...
	String str = string.toString().substring(beginIndex, limit);
    return (LineMetrics) new LineMetricsPCL(this, str);
}

/**
 * Override FontMetrics method.
 */
public LineMetrics getLineMetrics(char [] chars,
                                        int beginIndex, int limit,
                                        Graphics context) {
	return (LineMetrics) new LineMetricsPCL(this, new String(chars, beginIndex, limit));    
}

/**
 * Override FontMetrics method.
 */
public LineMetrics getLineMetrics(CharacterIterator iter,
                                        int beginIndex, int limit,
                                        Graphics context) {
    // make a string
    StringBuffer string = new StringBuffer();
 	for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
         string.append(c);
    }
	// in 1.1, StringBuffer doesn't have a substring() method...
	String str = string.toString().substring(beginIndex, limit);
    return (LineMetrics) new LineMetricsPCL(this, str);
}

//////////////// Some FontPCL only methods //////////////////////



/**
 * Return the height of this font in the indicated units where height is
 * ascender - descender.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the size of this font in the indicated units
 */
protected double getHeight(JCUnit units, int pointSize) {
	// these will already be in points
	return getAscent() - getDescent();
}

/**
 * Return the scaled xHeight for this font in the indicated units.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the xHeight of this font in the indicated units
 */
protected double getXHeight(JCUnit units, int pointSize) {
	double xHeightInPoints = convertToDecipoints(((FontPCL)font).getXHeight(),pointSize);
	return convertFromDeciPoints(units, xHeightInPoints);
}

/**
 * Return the scaled ascender for this font in the indicated units.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the ascender of this font in the indicated units
 */
protected double getAscender(JCUnit units, int pointSize) {
	TypeFacePCL tf = ((FontPCL)font).getTypeface();
	int ascent = tf.typefaceMetrics.getLowercaseAscent();
	double ascenderInPoints = convertToDecipoints(ascent,pointSize);
	return convertFromDeciPoints(units, ascenderInPoints);
}

/**
 * Return the scaled descender for this font in the indicated units.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the descender of this font in the indicated units
 */
protected double getDescender(JCUnit units, int pointSize) {
	TypeFacePCL tf = ((FontPCL)font).getTypeface();
	int descent = tf.typefaceMetrics.getLowercaseDescent();
	double descenderInPoints = convertToDecipoints(descent, pointSize);
	return convertFromDeciPoints(units, descenderInPoints);
}

/**
 * Given 'value' in points, convert it to 'units'.
 * @param toUnits the JCUnit in which the result should be returned
 * @param value the original value in 'points'
 * @return value converted to 'units'
 */
protected double convertFromDeciPoints(JCUnit units, double value) {
	double converted = 0;
	// put decipoints in terms of points so we can use JCUnit
	value = convertWorldToPoints(value);

	// now do the normal calculations
	if (units == JCUnit.POINTS) {
    	converted = value;
	}
	else if (units == JCUnit.INCHES) {
		converted = JCUnit.getAsInches(JCUnit.POINTS, value);
	}
	else if (units == JCUnit.CM) {
		converted = JCUnit.getAsCentimeters(JCUnit.POINTS, value);
	}
	else {
		throw new
			java.lang.IllegalArgumentException("Unknown units " + units);
	}
	return converted;
}

/**
 * Returns the width of this character scaled to the indicated point
 * size in the indicated units.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the width of this char in the indicated units
 */
protected double charWidth(JCUnit units, int pointSize, char ch) {
	int w = charWidth(ch);
	double charWidth = convertToDecipoints(w, pointSize);
	return convertFromDeciPoints(units, charWidth);
}

/**
 * Returns the total advance width for showing the specified String
 * in this Font taking into account kerning.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the width of this string in the indicated units
 */
protected double stringWidth(JCUnit units, int pointSize, String string) {
    double width = 0.0;
	CharSetEntryPCL currentChar, previousChar = null;

	TypeFacePCL typeface = ((FontPCL)font).getTypeface();
	int cpi = ((FontPCL)font).getCharsPerInch();
	if (cpi < 0) {
		for (int i = 0; i < string.length(); i++) {
    		currentChar	= ((FontPCL)font).getCharMetric(string.charAt(i));
			width		+= currentChar.getMetrics().getHorizontalEscapement();
			// see if there is kern data for this combination of characters
			if (previousChar != null && previousChar.getKerns().size() > 0) {
				Integer key = new Integer(currentChar.getIndex());
				KernPairPCL kern = previousChar.getKernPair(key);
				if (kern != null) {
					width += kern.kernValue;
				}
			}
			previousChar = currentChar;
		}
		double widthInPoints = convertToDecipoints(width, pointSize);
		return convertFromDeciPoints(units, widthInPoints);
    } else {
		double widthInPoints = 720.0 * (double) string.length() / (double) cpi;
		return convertFromDeciPoints(units, widthInPoints);
	}

}

/**
 * Returns the leading space for the first character of this String.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the width of the leading space in the indicated units
 */
protected double getLeadingSpace(JCUnit units, int pointSize, String string) {
	CharacterPCL metric =
		((FontPCL)font).getCharMetric(string.charAt(0)).getMetrics();
	double width = 0.0;
	if (metric != null) {
		if (metric.getLeftExtent() < 0) {
			width = - metric.getLeftExtent();
			width = convertToDecipoints(width, pointSize);
		}
	}
	return convertFromDeciPoints(units, width);
}

/**
 * Returns the trailing space for the first character of this String.
 * @param toUnits the JCUnit in which the result should be returned
 * @param pointSize the int pointSize for scaling this font
 * @return the width of the trailing space in the indicated units
 */
protected double getTrailingSpace(JCUnit units, int pointSize, String string) {
	int end = string.length() - 1;
	CharacterPCL metric =
		((FontPCL)font).getCharMetric(string.charAt(end)).getMetrics();
	double width = 0.0;
	if (metric != null) {
		if (metric.getHorizontalEscapement() <
			metric.getLeftExtent() + metric.getRightExtent()) {
			width = - metric.getLeftExtent() + metric.getRightExtent() -
					metric.getHorizontalEscapement();
			width = convertToDecipoints(width, pointSize);
		}
	}
	return convertFromDeciPoints(units, width);
}

/* Convert to decipoints (PCL driver units) */
double convertToDecipoints(double dimension, int font_size) {
	TypeFacePCL typeface = ((FontPCL)font).getTypeface();
	MetricsPCL metrics = typeface.getTypefaceMetrics();
	return((double) (dimension * font_size * 10) /
				(double) metrics.getDesignUnits());
}


double convertWorldToPoints(double world) {
	return(world / 10);
}

} // end of class
