
package jp.riken.brain.ni.samuraigraph.application;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.JobAttributes;
import java.awt.PageAttributes;
import java.awt.PrintJob;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.awt.print.PrinterJob;
import java.io.File;
import java.util.Properties;

import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.print.PrintService;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import jp.riken.brain.ni.samuraigraph.base.SGDialog;
import jp.riken.brain.ni.samuraigraph.base.SGDrawingWindow;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;
import jp.riken.brain.ni.samuraigraph.base.SGIImageExportManager;
import jp.riken.brain.ni.samuraigraph.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;

import org.freehep.graphics2d.VectorGraphics;
import org.freehep.graphicsio.FontConstants;
import org.freehep.graphicsio.ImageConstants;
import org.freehep.graphicsio.ImageGraphics2D;
import org.freehep.graphicsio.InfoConstants;
import org.freehep.graphicsio.PageConstants;
import org.freehep.graphicsio.cgm.CGMGraphics2D;
import org.freehep.graphicsio.emf.EMFGraphics2D;
import org.freehep.graphicsio.pdf.PDFGraphics2D;
import org.freehep.graphicsio.ps.PSGraphics2D;
import org.freehep.graphicsio.svg.SVGGraphics2D;
import org.freehep.graphicsio.swf.SWFGraphics2D;
import org.freehep.util.UserProperties;
import org.freehep.util.export.ExportDialog;


/**
 * A class used to preview, print and export an image.
 */
public class SGImageExportManager
	implements SGIImageExportManager, SGIConstants
{

	/**
	 * 
	 */
	public static final String DEFAULT_EXPORT_FILE_NAME = "export";
	
	
	/**
	 * 
	 */
	public static final String DEFAULT_TITLE_NAME = "Export view as ...";


	/**
	 * Static constants for VectorGraphics
	 */
	private static final String PREFIX = ImageGraphics2D.rootKey + ".";
	private static final String BG_SUFFIX = "." + PageConstants.BACKGROUND;
	private static final String BG_COLOR_SUFFIX = "." + PageConstants.BACKGROUND_COLOR;
	private static final String GIF = "gif";
	private static final String JPG = "jpg";
	private static final String PNG = "png";
	private static final String RAW = "raw";
	private static final String PPM = "ppm";
	private static final String[][] BG_KEY_ARRAY = {
		{ CGMGraphics2D.BACKGROUND, CGMGraphics2D.BACKGROUND_COLOR },
		{ PSGraphics2D.BACKGROUND, PSGraphics2D.BACKGROUND_COLOR },
		{ SWFGraphics2D.BACKGROUND, SWFGraphics2D.BACKGROUND_COLOR },
		{ PDFGraphics2D.BACKGROUND, PDFGraphics2D.BACKGROUND_COLOR },
		{ SVGGraphics2D.BACKGROUND, SVGGraphics2D.BACKGROUND_COLOR },
		{ EMFGraphics2D.BACKGROUND, EMFGraphics2D.BACKGROUND_COLOR },
		{ PREFIX + GIF + BG_SUFFIX, PREFIX + GIF + BG_COLOR_SUFFIX },
		{ PREFIX + JPG + BG_SUFFIX, PREFIX + JPG + BG_COLOR_SUFFIX },
		{ PREFIX + PNG + BG_SUFFIX, PREFIX + PNG + BG_COLOR_SUFFIX },
		{ PREFIX + RAW + BG_SUFFIX, PREFIX + RAW + BG_COLOR_SUFFIX },
		{ PREFIX + PPM + BG_SUFFIX, PREFIX + PPM + BG_COLOR_SUFFIX }
	};


	/**
	 * An export dialog.
	 */
	private final ExportDialog mExportDialog = new ExportDialog();


	/**
	 * 
	 */
	private String mBaseDirectoryName = null;


	/**
	 * 
	 */
	private String mExportFileName = DEFAULT_EXPORT_FILE_NAME;


	/**
	 * 
	 */
	private Component mTarget = null;


	/**
	 * Default constructor.
	 *
	 */
	public SGImageExportManager()
	{
		ExportDialogActionListener bl = new ExportDialogActionListener();
		this.mExportDialog.addActionListener(bl);
	}


	private static final String PREVIEW_DIALOG_TITLE = "Export as Image";


	/**
	 * Export as image the target object.
	 * @param target - target object for export
	 * @param owner - owner object
	 * @param w - width of image
	 * @param h - height of image
	 * @param silent - flag for the silent mode
	 * @return true:succeeded, false:failed
	 */
	public boolean export(
		Component target, Frame owner, final int w, final int h,
		final boolean silent )
	{

		this.mTarget = target;

		// preview
		if( !silent )
		{
			if( this.preview(
				target, owner, w, h, w, h,
				PREVIEW_DIALOG_TITLE,
				SGDialog.NEXT_BUTTON_TEXT, SGDialog.CANCEL_BUTTON_TEXT,
				silent
				) == CANCEL
			)
			{
				return true;
			}
		}

		if( this.mBaseDirectoryName==null )
		{
			this.mBaseDirectoryName = System.getProperty("user.dir");
		}

		ExportDialog ed = mExportDialog;
		Color bg = target.getBackground();
		Properties p = ed.getUserProperties();


		// directory
		String key = ExportDialog.SAVE_AS_FILE;
		String baseDir = this.mBaseDirectoryName;
		String path = ed.getUserProperties().getProperty(key);
		if( path!=null )
		{
			String parent = new File(path).getParent();
			String pathNew = baseDir + FILE_SEPARATOR + this.mExportFileName;
			ed.setUserProperty( key, pathNew );
		}
		String defFile = baseDir + FILE_SEPARATOR + this.mExportFileName;


		// background color
		String[][] bgArray = BG_KEY_ARRAY;
		for( int ii=0; ii<bgArray.length; ii++ )
		{
			String[] array = bgArray[ii];
			UserProperties.setProperty( p, array[0], true );
			UserProperties.setProperty( p, array[1], bg );
		}


		// show the modal export dialog
		ed.showExportDialog
		(
			owner,
			DEFAULT_TITLE_NAME,
			target,
			defFile
		);

		// get current directory
		String path_ = p.getProperty(key);
		if( path_!=null )
		{
			String parent = new File( path_ ).getParent();
			String name = path_.substring( parent.length() + 1 );
			this.setBaseDirectory( parent );
			this.mExportFileName = name;
		}

		return true;
	}


	// "International", "A3", "A4", "A5", "A6",
	// "Letter", "Legal", "Executive" or "Ledger";
	public static String[] getSizeList()
	{
		return PageConstants.getSizeList();
	}

	// "No Margin", "Small", "Medium" or "Large"
	public static String[] getMarginList()
	{
		return new String[]{
			PageConstants.NO_MARGIN,
			PageConstants.SMALL,
			PageConstants.MEDIUM,
			PageConstants.LARGE };
	}

	// "Portrait" or "Landscape"
	public static String[] getOrientationList()
	{
		return PageConstants.getOrientationList();
	}

	// "Type1" or "Type3"
	public static String[] getEmbedFontsAsList()
	{
		return FontConstants.getEmbedFontsAsList();
	}





	/**
	 * 
	 * @param target
	 * @param w
	 * @param h
	 * @param format
	 * @param path
	 * @return
	 */
	public boolean export(
		final Component target, final int w, final int h,
		final String type, final String path,
		final Properties prop )
	{
		File f = new File(path);
		String format = type.toUpperCase();
		Dimension dim = new Dimension(w,h);
		VectorGraphics g = null;
		boolean clipFlag = true;

		try
		{
			UserProperties p = new UserProperties();
			if( format.equals("EMF") )
			{
				g = new EMFGraphics2D( f, dim );

				// "Transparent"
				this.setBoolean(
					prop, PageConstants.TRANSPARENT,
					p, EMFGraphics2D.TRANSPARENT );

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, EMFGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, EMFGraphics2D.BACKGROUND_COLOR );

			}
			else if( format.equals("EPS") | format.equals("PS") )
			{
				g = new PSGraphics2D( f, dim );
				clipFlag = false;

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, PSGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, PSGraphics2D.BACKGROUND_COLOR );

				// "PageSize"
				this.setString(
					prop, PageConstants.PAGE_SIZE,
					p, PSGraphics2D.PAGE_SIZE,
					getSizeList() );

				// "PageMargins"
				this.setMargin(
					prop, PageConstants.PAGE_MARGINS,
					p, PSGraphics2D.PAGE_MARGINS );

				// "Orientation"
				this.setString(
					prop, PageConstants.ORIENTATION,
					p, PSGraphics2D.ORIENTATION,
					getOrientationList() );

				// "FitToPage"
				this.setBoolean(
					prop, PageConstants.FIT_TO_PAGE,
					p, PSGraphics2D.FIT_TO_PAGE );

				// "EmbedFonts"
				this.setBoolean(
					prop, FontConstants.EMBED_FONTS,
					p, PSGraphics2D.EMBED_FONTS );

				// "EmbedFontsAs"
				this.setString(
					prop, FontConstants.EMBED_FONTS_AS,
					p, PSGraphics2D.EMBED_FONTS_AS,
					getEmbedFontsAsList() );

				// "For"
				this.setString(
					prop, InfoConstants.FOR,
					p, PSGraphics2D.FOR );

				// "Title"
				this.setString(
					prop, InfoConstants.TITLE,
					p, PSGraphics2D.TITLE );

				// "WriteImagesAs"
				// "Smallest Size", "ZLIB" or "JPG"
				this.setString(
					prop, ImageConstants.WRITE_IMAGES_AS,
					p, PSGraphics2D.WRITE_IMAGES_AS,
					new String[]{ ImageConstants.SMALLEST, ImageConstants.ZLIB, ImageConstants.JPG } );

				// only for EPS
				if( format.equals("EPS") )
				{
					// "Preview"
					this.setBoolean(
						prop, "Preview",
						p, PSGraphics2D.PREVIEW );

					// "PreviewBits"
					this.setInteger(
						prop, "PreviewBits",
						p, PSGraphics2D.PREVIEW_BITS );
				}

			}
			else if( format.equals("PDF") )
			{
				g = new PDFGraphics2D( f, dim );
				clipFlag = false;

				// "Transparent"
				this.setBoolean(
					prop, PageConstants.TRANSPARENT,
					p, PDFGraphics2D.TRANSPARENT );

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, PDFGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, PDFGraphics2D.BACKGROUND_COLOR );

				// "Version"
				this.setString(
					prop, "Version",
					p, PDFGraphics2D.VERSION,
					new String[]{ PDFGraphics2D.VERSION4, PDFGraphics2D.VERSION5 } );

				// "Thumbnails"
				// set by PDF version
				String version = p.getProperty( PDFGraphics2D.VERSION );
				if( version!=null )
				{
					p.setProperty(
						PDFGraphics2D.THUMBNAILS,
						version.equals( PDFGraphics2D.VERSION4 ) );
				}

				// "Compress"
				this.setBoolean(
					prop, "Compress",
					p, PDFGraphics2D.COMPRESS );

				// "PageSize"
				this.setString(
					prop, PageConstants.PAGE_SIZE,
					p, PDFGraphics2D.PAGE_SIZE,
					getSizeList() );

				// "PageMargins"
				this.setMargin(
					prop, PageConstants.PAGE_MARGINS,
					p, PDFGraphics2D.PAGE_MARGINS );

				// "Orientation"
				this.setString(
					prop, PageConstants.ORIENTATION,
					p, PDFGraphics2D.ORIENTATION,
					getOrientationList() );

				// "FitToPage"
				this.setBoolean(
					prop, PageConstants.FIT_TO_PAGE,
					p, PDFGraphics2D.FIT_TO_PAGE );

				// "EmbedFonts"
				this.setBoolean(
					prop, FontConstants.EMBED_FONTS,
					p, PDFGraphics2D.EMBED_FONTS );

				// "EmbedFontsAs"
				this.setString(
					prop, FontConstants.EMBED_FONTS_AS,
					p, PDFGraphics2D.EMBED_FONTS_AS,
					getEmbedFontsAsList() );

				// "WriteImagesAs"
				// "Smallest Size", "ZLIB" or "JPG"
				this.setString(
					prop, ImageConstants.WRITE_IMAGES_AS,
					p, PDFGraphics2D.WRITE_IMAGES_AS,
					new String[]{ ImageConstants.SMALLEST, ImageConstants.ZLIB, ImageConstants.JPG } );

				// "Author"
				this.setString(
					prop, InfoConstants.AUTHOR,
					p, PDFGraphics2D.AUTHOR );

				// "Title"
				this.setString(
					prop, InfoConstants.TITLE,
					p, PDFGraphics2D.TITLE );

				// "Subject"
				this.setString(
					prop, InfoConstants.SUBJECT,
					p, PDFGraphics2D.SUBJECT );

				// "Keywords"
				this.setString(
					prop, InfoConstants.KEYWORDS,
					p, PDFGraphics2D.KEYWORDS );

			}
			else if( format.equals("SVG") )
			{
				g = new SVGGraphics2D( f, dim );
				clipFlag = true;

				String value;

				// "Transparent"
				this.setBoolean(
					prop, PageConstants.TRANSPARENT,
					p, SVGGraphics2D.TRANSPARENT );

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, SVGGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, SVGGraphics2D.BACKGROUND_COLOR );

				// "Version"
				this.setString(
					prop, "Version",
					p, SVGGraphics2D.VERSION,
					new String[]{ SVGGraphics2D.VERSION_1_0, SVGGraphics2D.VERSION_1_1 } );

				// "Binary"
				this.setBoolean(
					prop, "Binary",
					p, SVGGraphics2D.COMPRESS );

				// "Stylable"
				this.setBoolean(
					prop, "Stylable",
					p, SVGGraphics2D.STYLABLE );

				// "ImageSize"
				this.setDimension(
					prop, ImageConstants.IMAGE_SIZE,
					p, SVGGraphics2D.IMAGE_SIZE );

				// "ExportImages"
				this.setBoolean(
					prop, "ExportImages",
					p, SVGGraphics2D.EXPORT_IMAGES );

				// "ExportSuffix"
				this.setString(
					prop, "ExportImages",
					p, SVGGraphics2D.EXPORT_SUFFIX );

				// "WriteImagesAs"
				// "Smallest Size", "PNG" or "JPG"
				this.setString(
					prop, ImageConstants.WRITE_IMAGES_AS,
					p, SVGGraphics2D.WRITE_IMAGES_AS,
					new String[]{ ImageConstants.SMALLEST, ImageConstants.PNG, ImageConstants.JPG } );

				// "For"
				this.setString(
					prop, InfoConstants.FOR,
					p, SVGGraphics2D.FOR );

				// "Title"
				this.setString(
					prop, InfoConstants.TITLE,
					p, SVGGraphics2D.TITLE );

			}
			else if( format.equals("CGM") )
			{
				g = new CGMGraphics2D( f, dim );
				clipFlag = true;

				String value;

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, CGMGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, CGMGraphics2D.BACKGROUND_COLOR );

				// "Binary"
				this.setBoolean(
					prop, "Binary",
					p, CGMGraphics2D.BINARY );

				// "Author"
				this.setString(
					prop, InfoConstants.AUTHOR,
					p, CGMGraphics2D.AUTHOR );

				// "Title"
				this.setString(
					prop, InfoConstants.TITLE,
					p, CGMGraphics2D.TITLE );

				// "Subject"
				this.setString(
					prop, InfoConstants.SUBJECT,
					p, CGMGraphics2D.SUBJECT );

				// "Keywords"
				this.setString(
					prop, InfoConstants.KEYWORDS,
					p, CGMGraphics2D.KEYWORDS );

			}
			else if( format.equals("SWF") )
			{
				g = new SWFGraphics2D( f, dim );
				clipFlag = true;

				// "Transparent"
				this.setBoolean(
					prop, PageConstants.TRANSPARENT,
					p, SWFGraphics2D.TRANSPARENT );

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, SWFGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, SWFGraphics2D.BACKGROUND_COLOR );

				// "WriteImagesAs"
				// "Smallest Size", "ZLIB" or "JPG"
				this.setString(
					prop, ImageConstants.WRITE_IMAGES_AS,
					p, SWFGraphics2D.WRITE_IMAGES_AS,
					new String[]{ ImageConstants.SMALLEST, ImageConstants.ZLIB, ImageConstants.JPG } );

			}
			else if( format.equals("JPG")
				| format.equals("JPEG")
				| format.equals("GIF")
				| format.equals("PNG")
				| format.equals("RAW")
				| format.equals("PPM") )
			{
				g = new ImageGraphics2D( f, dim, format );
				clipFlag = true;

				String formatKey = ImageGraphics2D.rootKey+"."+format;

				// for the transparent format
				if( ImageGraphics2D.canWriteTransparent( format ) )
				{
					// "Transparent"
					this.setBoolean(
						prop, PageConstants.TRANSPARENT,
						p, ImageGraphics2D.TRANSPARENT );
				}

				// "Background"
				this.setBoolean(
					prop, PageConstants.BACKGROUND,
					p, formatKey+ImageGraphics2D.BACKGROUND );

				// "BackgroundColor"
				this.setColor(
					prop, PageConstants.BACKGROUND_COLOR,
					p, formatKey+ImageGraphics2D.BACKGROUND_COLOR );
				
				// "Antialias"
				this.setBoolean(
					prop, "Antialias",
					p, formatKey+ImageGraphics2D.ANTIALIAS );

				// "AntialiasText"
				this.setBoolean(
					prop, "AntialiasText",
					p, formatKey+ImageGraphics2D.ANTIALIAS_TEXT );

				// copy parameters from specific format
				ImageWriter writer = ImageGraphics2D.getPreferredImageWriter( format );
				if( writer!=null )
				{
					ImageWriteParam param = writer.getDefaultWriteParam();

//					// compression
//					if( param.canWriteCompressed() )
//					{
//						p.setProperty(
//							formatKey+ImageGraphics2D.COMPRESS_QUALITY,
//							param.getCompressionQuality() );
//					}
//					else
//					{
//						p.setProperty(
//							formatKey+ImageGraphics2D.COMPRESS_QUALITY,
//							0.0f );
//					}

					// progressive
					if( param.canWriteProgressive() )
					{
						p.setProperty( formatKey+ImageGraphics2D.PROGRESSIVE,
							param.getProgressiveMode()!=ImageWriteParam.MODE_DISABLED );
					}
					else
					{
						this.setBoolean(
							prop, "Progressive",
							p, formatKey+ImageGraphics2D.PROGRESSIVE );
					}
				}

			}
			else
			{
				return false;
			}

			g.setProperties(p);

		}
		catch( Exception e )
		{
			e.printStackTrace();
			return false;
		}

		if( target instanceof SGDrawingWindow.ExportPanel )
		{
			SGDrawingWindow.ExportPanel p
				= (SGDrawingWindow.ExportPanel)target;
			p.setClipFlag( clipFlag );
		}

		g.startExport(); 
		target.print(g); 
		g.endExport();

		return true;
	}


	//
	private boolean setString(
		final Properties prop, final String propKey,
		final UserProperties p, final String key )
	{
		String value = prop.getProperty( propKey );
		if( value==null )
		{
			return false;
		}
		p.setProperty( key, value );
		return true;
	}


	//
	private boolean setString(
		final Properties prop, final String propKey,
		final UserProperties p, final String key,
		final String[] candidates )
	{
		String value = prop.getProperty( propKey );
		if( value!=null )
		{
			for( int ii=0; ii<candidates.length; ii++ )
			{
				if( candidates[ii].equals( key ) )
				{
					p.setProperty( key, value );
					return true;
				}
			}
			return false;
		}
		return true;
	}



	//
	private boolean setBoolean(
		final Properties prop, final String propKey,
		final UserProperties p, final String key )
	{
		String value = prop.getProperty( propKey );
		if( value!=null )
		{
			Boolean b = SGUtilityText.getBoolean( value );
			if( b==null )
			{
				return false;
			}
			p.setProperty( key, b.booleanValue() );
		}
		return true;
	}

	//
	private boolean setInteger(
		final Properties prop, final String propKey,
		final UserProperties p, final String key )
	{
		String value = prop.getProperty( propKey );
		if( value!=null )
		{
			Integer n = SGUtilityText.getInteger( value );
			if( n==null )
			{
				return false;
			}
			p.setProperty( key, n.intValue() );
		}
		return true;
	}

	//
	private boolean setColor(
		final Properties prop, final String propKey,
		final UserProperties p, final String key )
	{
		String value = prop.getProperty( propKey );
		if( value!=null )
		{
			Color cl = SGUtilityText.getColor( value );
			if( cl==null )
			{
				return false;
			}
			p.setProperty( key, cl );
		}
		return true;
	}

	//
	private boolean setDimension(
		final Properties prop, final String propKey,
		final UserProperties p, final String key )
	{
		String value = prop.getProperty( propKey );
		if( value!=null )
		{
			Dimension dim = SGUtilityText.getDimension(value);
			if( dim==null )
			{
				return false;
			}
			p.setProperty( key, dim );
		}
		return true;
	}

	//
	private boolean setMargin(
		final Properties prop, final String propKey,
		final UserProperties p, final String key )
	{
		String value = prop.getProperty( propKey );
		if( value!=null )
		{
			Insets in = PageConstants.getMargins( value );
			if( in==null )
			{
				return false;
			}
			p.setProperty( key, in );
		}
		return true;
	}


	// An action listener class for the export file dialog.
	private class ExportDialogActionListener implements ActionListener
	{
		public void actionPerformed( ActionEvent e )
		{
			Object source = e.getSource();
			if( source instanceof JComboBox )
			{
				JComboBox cb = (JComboBox)source;
				Object obj = cb.getSelectedItem();

				Component target = SGImageExportManager.this.mTarget;
				if( target instanceof SGDrawingWindow.ExportPanel )
				{
					SGDrawingWindow.ExportPanel p
						= (SGDrawingWindow.ExportPanel)target;
					final boolean b
						= ( obj instanceof org.freehep.graphicsio.ps.EPSExportFileType )
						| ( obj instanceof org.freehep.graphicsio.ps.PSExportFileType )
						| ( obj instanceof org.freehep.graphicsio.pdf.PDFExportFileType );
					p.setClipFlag(!b);
				}
			}
		}
	}


	/**
	 * 
	 * @param dir
	 */
	public void setBaseDirectory( String dir )
	{
		this.mBaseDirectoryName = new String(dir);
	}


	/**
	 * 
	 * @return
	 */
	public String getBaseDirectory()
	{
		return this.mBaseDirectoryName;
	}



	/**
	 * The OK button is pressed.
	 */
	public static final int OK = 0;

	/**
	 * The Cancel button is pressed or the window is closed.
	 */
	public static final int CANCEL = 1;


	/**
	 * Preview image of the target object.
	 * @param target - target object for export
	 * @param owner - owner object
	 * @param w - width of image
	 * @param h - height of image
	 * @return status
	 */
	private int preview(
		Component target, Frame owner,
		final int imageWidth, final int imageHeight,
		final int paperWidth, final int paperHeight,
		final String title, final String textOK, final String textCancel,
		final boolean silent )
	{
		// create and show a modal preview dialog
		SGPreviewDialog dg = this.createPreviewDialog(
			target, owner, imageWidth, imageHeight, paperWidth, paperHeight,
			title, !silent );
		dg.setOKButtonText( textOK );
		dg.setCancelButtonText( textCancel );

		if( !silent )
		{
			dg.setVisible(true);
		}

		final int ret = dg.getCloseOption();
		if( ret==SGIConstants.CANCEL_OPTION )
		{
			return CANCEL;
		}
		
		return OK;
	}



	/**
	 * Print image of the target object.
	 * @param target - target object for export
	 * @param owner - owner object
	 * @param w - width of image
	 * @param h - height of image
	 * @param silent - flag for the silent mode
	 * @return true:succeeded, false:failed
	 */
	public boolean print(
		final Component target, final Frame owner,
		final int w, final int h, final boolean silent )
	{
		// get available printers
		PrintService[] pArray = PrinterJob.lookupPrintServices();
		if( pArray.length==0 )
		{
			if( !silent )
			{
				SGUtility.showMessageDialog(
					owner, "No printer is available.", "Error",
					JOptionPane.ERROR_MESSAGE
				);
			}
			return false;
		}

		// dialog type
		JobAttributes.DialogType dType = silent ? JobAttributes.DialogType.NONE : JobAttributes.DialogType.NATIVE;

		// create attributes objects
		JobAttributes ja
			= new JobAttributes(
				1,
				JobAttributes.DefaultSelectionType.ALL,
				JobAttributes.DestinationType.PRINTER,
				dType,
				null,
				Integer.MAX_VALUE,
				1,
				JobAttributes.MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES,
				new int[][]{ new int[]{ 1, 1 } },
				null,
				JobAttributes.SidesType.ONE_SIDED
			);

		PageAttributes pa
			= new PageAttributes(
				PageAttributes.ColorType.MONOCHROME,
				PageAttributes.MediaType.A4,
				PageAttributes.OrientationRequestedType.PORTRAIT,
				PageAttributes.OriginType.PHYSICAL,
				PageAttributes.PrintQualityType.HIGH,
				new int[]{ 72, 72, 3 }
			);


		//
		// preview
		//

		String title = "Print as Image";
		PrintJob job = null;
//		while( true )
		{
			job = Toolkit.getDefaultToolkit().getPrintJob(
				owner, "", 
//				null,
				ja,
//				null
				pa
			);
			if( job==null )
			{
				// returned value is equal to null when the print job is canceled
				return true;
			}

			Dimension paperSize = job.getPageDimension();
			final int ret = this.preview(
				target, owner, w, h,
				paperSize.width, paperSize.height,
				title,
				"Print",
				SGDialog.CANCEL_BUTTON_TEXT, silent );
			if( ret==OK )
			{
//				break;
			}
			else if( ret==CANCEL )
			{
				return true;
			}
		}


		//
		// print
		//

		// get a Graphics object to print
		Graphics g = job.getGraphics();

		// print objects
		target.printAll(g);

		//dispose a graphic object
		g.dispose();

		// end the print job
		job.end();

		return true;
	}



	/**
	 * Create a preview dilaog.
	 * @param target - target object for export
	 * @param owner - owner object
	 * @param w - width of image
	 * @param h - height of image
	 * @param modal - whether the preview dialog is modal
	 * @return true:succeeded, false:failed
	 */
	private SGPreviewDialog createPreviewDialog(
		Component target, Frame owner,
		final int imageWidth, final int imageHeight,
		final int paperWidth, final int paperHeight,
		final String title,
		final boolean modal )
	{
		SGPreviewDialog dg = new SGPreviewDialog( owner, title, modal );
		dg.pack();

		dg.setLocation( owner.getLocation() );
		dg.setTargetObject(
			target, imageWidth, imageHeight, paperWidth, paperHeight,
			target.getBackground() );

		return dg;		
	}



	/**
	 * Main method.
	 * @param args
	 */
	public static void main( String[] args ) throws Exception
	{
		SGImageExportManager man = new SGImageExportManager();
		ExportTestPanel panel = new ExportTestPanel();

//		man.export( p, null, p.getWidth(), p.getHeight(), true );

		File f = new File( "C:\\Tmp\\test.emf" );
		f.createNewFile();

		Properties p = new Properties();
		p.setProperty( "PageSize", "A5" );
		VectorGraphics g = new EMFGraphics2D( f, new Dimension( panel.getSize() ));
		g.setProperties(p); 

		g.startExport(); 
		panel.print(g); 
		g.endExport();

	}


	/**
	 * A class for testing.
	 *
	 */
	private static class ExportTestPanel extends JPanel
	{
		private ExportTestPanel()
		{
			super();
			this.init();
		}

		private void init()
		{
			this.setSize( 800, 600 );
			this.setOpaque(false);
		}

		public void paintComponent( Graphics g )
		{
			super.paintComponent(g);
			Graphics2D g2d = (Graphics2D)g;
			final float width = 24.0f;

			Rectangle rect = new Rectangle( 55, 40, 350, 420 );
			g2d.setStroke(
				new BasicStroke(
					1.0f,
					BasicStroke.CAP_BUTT,
					BasicStroke.JOIN_ROUND
				)
			);
			g2d.setPaint( Color.BLUE );
			g2d.draw(rect);

			Line2D line0 = new Line2D.Float( 0.0f, 0.0f, 220.0f, 700.0f );
			g2d.setPaint( Color.YELLOW );
			g2d.draw(line0);

			g2d.setClip(rect);

			Line2D line1 = new Line2D.Float( 0.0f, 0.0f, 420.0f, 350.0f );
			g2d.setPaint( Color.BLUE );
			g2d.draw(line1);

			g2d.setClip( new Rectangle( 0, 0, 500, 600 ) );

			Line2D line2 = new Line2D.Float( 0.0f, 0.0f, 420.0f, 560.0f );
			g2d.setPaint( Color.GREEN );
			g2d.draw(line2);
		}

	}


}

