view at/ofai/music/util/PSPrinter.java @ 5:bcb4c9697967 tip

Add README and CITATION files
author Chris Cannam
date Tue, 03 Dec 2013 12:58:05 +0000
parents 4c3f5bc01c97
children
line wrap: on
line source
/*
	Copyright (C) 2001, 2006 by Simon Dixon

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program (the file gpl.txt); if not, download it from
	http://www.gnu.org/licenses/gpl.txt or write to the
	Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

package at.ofai.music.util;

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

import at.ofai.music.util.Format;

/** A utility class for converting graphical user interface components to
 *  PostScript, which can be sent directly to a printer or printed to a file.
 *  This gives much higher quality illustrations for articles
 *  than if a screenshot is used, since scaling should not reduce quality.
 *  The only requirement is that the component to be printed has a
 *  <code>paint(Graphics)</code> method.
 *  <p>There are some bugs in this code which require manual editing of
 *  the PostScript file. First, there doesn't seem to be any way to include
 *  the bounding box, although it is possible to calculate it. Second, the
 *  cliprect produced in the PostScript output is wrong.
 *  (Check: has this been fixed in more recent Java versions?
 *  Apparently not, as of 1.5.0, but if scaling is not performed, the cliprect
 *  is OK and the bounding box correct.)
 *  See {@link PSPrinter#print(Graphics, PageFormat, int)}
 */
public class PSPrinter implements Printable {

	/** the component to be converted */
	Component component;
	/** the desired graphical resolution in pixels per inch */
	int resolution;	// can't work out how to ask the system

	/** Print a GUI component to a PostScript printer or file.
	 *  The 2 forms of this method are the normal ways of accessing this class.
	 *  This form has problems printing some components. It is recommended to
	 *  use the other version.
	 *  @param c the component to be rendered in PostScript
	 *  @param r the resolution of the printer in pixels per inch
	 */
	public static void print(Component c, int r) {
		new PSPrinter(c, r).doPrint();
	}

	/** Print a GUI component to a PostScript printer or file.
	 *  The 2 forms of this method are the normal ways of accessing this class.
	 *  If no resolution is given, the picture is not scaled, and the cliprect
	 *  is then correct. This is the recommended version to use.
	 *  @param c the component to be rendered in PostScript
	 */
	public static void print(Component c) {
		new PSPrinter(c, -1).doPrint();
	}
	
	/** Constructs a PSPrinter for a given graphical component and resolution.
	 *  @param c   the component to be rendered in PostScript
	 *  @param res the resolution of the printer in pixels per inch; set res to
	 *             -1 for no scaling (avoids the apparently buggy cliprect)
	 */
	public PSPrinter(Component c, int res) {
		component = c;
		resolution = res;
	} // constructor

	/** Produces a print dialog and executes the requested print job.
	 *  The print job performs its task by callback of the
	 *  {@link PSPrinter#print(Graphics, PageFormat, int)} method.
	 */
	public void doPrint() {
		PrinterJob printJob = PrinterJob.getPrinterJob();
		printJob.setPrintable(this);	// tell it where the rendering code is
		if (printJob.printDialog()) {
			try {
				printJob.print();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	} // doPrint()

	/** The callback method for performing the printing / Postscript conversion.
	 *  The resulting PostScript file requires some post-editing.
	 *  In particular, there are two problems to be dealt with:
	 *  <p>1) The file has no bounding box. This method prints the correct
	 *  bounding box to stardard output, and it must then be cut and pasted
	 *  into the PostScript file. (There must be a better way!)
	 *  <p>2) The cliprect is wrong (but only if the resolution is specified).
	 *  This is solved by using resolution = -1 or by deleting the lines
	 *  in the PostScript file from <code>newpath</code> to <code>clip</code>.
	 *  (I don't know if this causes problems for components that try to draw
	 *  outside of their area.)
	 *  @param g  the graphics object used for painting
	 *  @param f  the requested page format (e.g. A4)
	 *  @param pg the page number (must be 0, or we report an error)
	 *  @return   the error status; if the page is successfully rendered,
	 *  Printable.PAGE_EXISTS is returned, otherwise if a page number greater
	 *  than 0 is requested, Printable.NO_SUCH_PAGE is returned
	 *  @throws   PrinterException thrown when the print job is terminated
	 */
	public int print(Graphics g, PageFormat f, int pg) throws PrinterException {
		if (pg >= 1)
			 return Printable.NO_SUCH_PAGE;
		Graphics2D g2 = (Graphics2D) g;
		double wd = component.getWidth();
		double ht = component.getHeight();
		double imwd = f.getImageableWidth();
		double imht = f.getImageableHeight();
		double corr = resolution / 72.0;
		double scaleFactor = corr * Math.min(imwd / wd, imht / ht);
		double xmin = f.getImageableX();
		double ymin = f.getImageableY();
		AffineTransform scale = new AffineTransform(scaleFactor, 0,
													0, scaleFactor,
													corr * xmin, corr * ymin);
		Format.setGroupingUsed(false);
		double pgHt = f.getHeight();
		if (resolution > 0) {
			g2.setTransform(scale);
			System.out.println("%%BoundingBox: " +
					Format.d(xmin, 0) + " " +
					Format.d(pgHt - ymin - ht * scaleFactor / corr, 0) + " " +
					Format.d(xmin + wd * scaleFactor / corr, 0) + " " +
					Format.d(pgHt - ymin, 0));
		} else {
			g2.setClip(0, 0, (int)wd, (int)ht); 
			System.out.println("%%BoundingBox: " +
					Format.d(0, 0) + " " +
					Format.d(pgHt - ht, 0) + " " +
					Format.d(wd, 0) + " " +
					Format.d(pgHt, 0));
		}

		// System.out.println(f.getWidth() + " " + f.getHeight() + " " +
		// 				f.getImageableX() + " " + f.getImageableY() + " " +
		// 				f.getImageableWidth() + " " + f.getImageableHeight());
		// Letter = 8.5x11" 612x792pt  DEFAULT
		// A4 = 210x297mm   595x842pt

	//	AffineTransform scale = new AffineTransform(2.5, 0, 0, 2.5, 200, 1000);
	//	g2.setTransform(scale);		// The figures need some fiddling.
									// In particular, the PostScript file has:
									// 1) no bounding box (add manually)
									// 2) wrong cliprect (delete lines from
									//                    "newpath" to "clip")
		component.printAll(g2);
		return Printable.PAGE_EXISTS;
	} // print()

} // class PSPrinter