/* 
______________________________________________________________________
*
* 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: JCPCLPrinter.java,v $ $Revision: 1.2 $ $Date: 2006-03-17 21:13:25 $ $Locker:  $  Quest Software Inc.

package com.klg.jclass.page.pcl;

import com.klg.jclass.page.JCPrinter;
import com.klg.jclass.page.FontParser;



import java.util.Stack;
import java.util.Date;
import java.io.OutputStream;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Enumeration;




import java.awt.geom.Rectangle2D;


/**
 *
 * Implements the <code>PrinterDriver</code> interface.
 *
 */
public class JCPCLPrinter extends JCPrinter {

/** The last paper size that the output was set to accomodate. */
protected Rectangle2D oldPageSize = null;
/** Whether or not the printer was last set to be in GL2 mode. */
protected boolean isGL2Mode = false;
/** Stores the size of the current page. */
protected Rectangle2D currentPageSize;

// page sizes supported by hp printers in points
/** A letter-size page (one of the HP-supported paper sizes). */
protected static final Rectangle2D.Double letter
								= new Rectangle2D.Double(0, 0, 612, 792);
/** A legal-size page. (8.5x14) (one of the HP-supported paper sizes) */
protected static final Rectangle2D.Double legal
								= new Rectangle2D.Double(0, 0, 612, 1008);
/** An executive-size page (one of the HP-supported paper sizes). */
protected static final Rectangle2D.Double executive
								= new Rectangle2D.Double(0, 0, 522, 756);
/** A "ledger"-size page (11x17) (one of the HP-supported paper sizes). */
protected static final Rectangle2D.Double ledger
								= new Rectangle2D.Double(0, 0, 792, 1224);
/** A european A4-size page (one of the HP-supported paper sizes). */
protected static final Rectangle2D.Double a4
								= new Rectangle2D.Double(0, 0, 595.3, 841.9);
/** A european A3-size page (one of the HP-supported paper sizes). */
protected static final Rectangle2D.Double a3
								= new Rectangle2D.Double(0, 0, 841.9, 1190.6);

// PENDING -- what about envelope sizes??

/**
 *
 * Constructor.
 * Allows detailed specification of font creation. The constructor which takes
 * one argument is akin to calling this constructor with these parameters:
 * <pre>
 *		JCPrinter p = new JCPCLPrinter(
 *				someOutputStream,						// the output stream
 *				new com.klg.jclass.page.pcl.TFMParser(),	// the parser
 *				"/com/klg/jclass/page/pcl/fonts.jar", 	// jar location
 *				".tfm",									// file extension
 *				"com.klg.jclass.page.pcl.JCPCLFontMap");
 * </pre>
 *
 * <P>
 * <b>Note:</b> For large documents and for optimal performance, wrap
 * the output stream in a <code>BufferedOutputStream</code>. For example:
 * <pre>
 * 		BufferedOutputStream bos 	= new BufferedOutputStream(os, 2048);
 * 		JCPrinter printer 			= new JCPCLPrinter(bos, ...);
 * </pre>
 *
 *
 * @param os 			the <code>OutputStream</code> to which formatted output should be sent
 * @param parser 		the <code>FontParser</code> which creates fonts from tfm files
 * @param jarLocation 	the location of the jar containing tfm files relative
 *						to the class loader including the name of the jar file
 * @param fileExtension	the parser will turn files contained in "jarLocation"
 *						ending with this extension into fonts
 * @param fontMapFileName the user fontmap file; a file containing mappings
 *						between font names and their aliases
 *
 *
 */
public JCPCLPrinter(OutputStream os, FontParser parser, String jarLocation,
		String fileExtension, String fontMapFileName) {
	this.os = os;

	// force fonts to be loaded
	FontPCL.getAllFonts(parser, jarLocation, fileExtension, fontMapFileName);
}

/**
 * Creates a PCL printer given an <code>OutputStream</code> to write the data to.
 * <P>
 * <b>Note:</b> For large documents and for optimal performance, wrap
 * the output stream in a <code>BufferedOutputStream</code>. For example:
 * <pre>
 * 		BufferedOutputStream bos 	= new BufferedOutputStream(os, 2048);
 * 		JCPrinter printer 			= new JCPCLPrinter(bos);
 * </pre>
 *
 * @param os the <code>OutputStream</code> to which to write the PCL data
 */
public JCPCLPrinter(OutputStream os) {
	this.os = os;

	// force fonts to be loaded
	FontPCL.getAllFonts();
}

/**
 * Creates default styles, any other one-time initialization
 * (for example, builds font families from available fonts.).
 */
public void setup() {
	// no PCL-specific setup required
}

/**
 * Returns <code>true</code> since PCL printer does own image scaling.
 * @return <code>true</code> since PCL printer does own image scaling
 */
public boolean isImageScalable()
{
    return true;
}

/**
 * Writes the PCL document header and setup information.
 * @param pageSize the size of the first/default page
 */
public void openDocument(Rectangle2D pageSize) {

	super.openDocument(pageSize);

	// force the font def to be written out between runs
	Graphics g = getGraphics();
	if (g != null) {
		((Graphics2DPCL)g).resetFont = true;
	}

	// Output the job start (printer reset) commands
	output("\033%-12345X");
	output("\033E");

	// Set the printer units
	output("\033&u720D");

	// setup the dimensions of the page
	setupPage(pageSize);
}

/**
 * Completes the print job.
 * @param pageSize the size of the default/last page
 */
public void closeDocument(Rectangle2D pageSize) {

	// If we're in line-drawing mode, exit it
	if (graphics != null && isGL2Mode) {
		((Graphics2DPCL)graphics).exitGl2Mode();
	}

	// End the PCL job
	output("\033E");
	output("\033%-12345X");

	super.closeDocument(pageSize);
	graphics.dispose();
}

/**
 * Begins a new page in the document; establishes the co-ordinate system,
 * sets margins and clipping, etc.
 * @param pageSize the size of the new page
 * @param pageNumber the number of the new page in the document
 */
public void beginPage(Rectangle2D pageSize, int pageNumber) {

	super.beginPage(pageSize, pageNumber);

	// If we're in line-drawing mode, exit it
	if (graphics != null && isGL2Mode) {
		((Graphics2DPCL)graphics).exitGl2Mode();
	}

	// Compute the margin of the unprintable part of the page
	// 90 deci units is unprintable margin
	double margin_space = 90.0;

	// if the paper size/orientation has changed, update the paper settings
	if (paperChanged(pageSize)) {
		setupPage(pageSize);
	}

	// initialise the clipping rectangle
	((Graphics2DPCL) getGraphics()).initialiseClip(pageSize);
}

/**
 * Outputs the commands to size and orients the page.
 * @param pageSize the dimensions of the current page
 */
protected void setupPage(Rectangle2D pageSize) {

	

	// Store the page size for the printer to refer to later
	currentPageSize = (Rectangle2D) pageSize.clone();

	// Output Letter, Legal, Executive, Ledger, A4 or A3
	// code which are the hp supported paper sizes
	int paperCode = getPaperCode(pageSize.getWidth(), pageSize.getHeight());

	// Output page setup code as required
    output("\033&l" + paperCode + "A");

	// handle landscape
	if (isLandscape(pageSize.getWidth(), pageSize.getHeight())) {
		output("\033&l1O");
	}
	else {
		// portrait
		output("\033&l0O");
	}

	// Set the top margin and Vertical Motion Index (VMI) to zero
	output("\033&l0e0C");

	// set the current point to the page origin of picture frame (19-18)
	output("\033*p0x0Y");

	// Define size of the HP-GL/2 picture frame and attach it (19-18)
	String 	string = "\033*c"
					 + ((pageSize.getX() + pageSize.getWidth())
					 								* Graphics2DPCL.DECIPOINT)
					 + "x"
					 + ((pageSize.getY() + pageSize.getHeight())
					 								* Graphics2DPCL.DECIPOINT)
					 + "Y\n\033*c0T";
	output(string);
}

/**
 * Given width and height, returns the hp paper code.
 * Since width and height may be imprecise because
 * of conversion between units -- be forgiving.
 * If paper is wider than tall, we change width and
 * height so we only deal in portrait.
 * @param width the width of the page in points
 * @param height the height of the page in points
 * @return a code denoting the paper size
 */
protected int getPaperCode(double width, double height) {
	if (width > height) {
		double tmp = width;
		width = height;
		height = tmp;
	}

	// default to letter
	int paperCode = 2;

	// educated fudge factor
	width -= 9;
	height -= 9;

	// check starting with smallest paper size and work larger
	if (executive.contains(width, height)) {
		paperCode = 1;
	}
	else if (a4.contains(width, height)) {
		paperCode = 26;
	}
	else if (letter.contains(width, height)) {
		paperCode = 2;
	}
	else if (legal.contains(width, height)) {
		paperCode = 3;
	}
	else if (ledger.contains(width, height)) {
		paperCode = 6;
	}
	else if (a3.contains(width, height)) {
		paperCode = 27;
	}
	return paperCode;
}

/**
 * Completes the page and advances to the next one.
 * @param pageSize the size of the current page
 */
public void endPage(Rectangle2D pageSize) {

	// If we're in line-drawing mode, exit it
	if (graphics != null && isGL2Mode) {
		((Graphics2DPCL)graphics).exitGl2Mode();
	}

	// Print a form feed
	output("\014");

	super.endPage(pageSize);

}

/**
 * Returns a graphics objects associated with this printer.
 * @return a Graphics object to use to write to the PCL printer
 */
public Graphics getGraphics() {
	if (graphics == null) {
		graphics = new Graphics2DPCL(os, this);
		((Graphics2DPCL) graphics).setFrameRoot();
	}
	return (Graphics) graphics;
}

///////////////////////////////////////////////////

/**
 * Determines if the paper size or orientation has changed.
 * @param pageSize the size of the next page to output
 * @return <code>true</code> if the next page is different from the old page
 */
protected boolean paperChanged(Rectangle2D pageSize) {

	// if there is no previous page, store the current page size
	if (oldPageSize == null) {
		oldPageSize = (Rectangle2D) pageSize.clone();
		return(false);
	}

	return(!oldPageSize.equals(pageSize));
}

} // end class
