/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.tools; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.rmi.RemoteException; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import org.jibble.epsgraphics.EpsGraphics2D; import org.opensourcephysics.display.OSPRuntime; import org.opensourcephysics.display.Renderable; import org.opensourcephysics.media.gif.GIFEncoder; /** * This provides a simple way to capture screen images. * * @author Francisco Esquembre (http://fem.um.es) * @author Wolfgang Christian * @version 1.0 */ public class SnapshotTool implements Tool { // ---- Localization static private final String BUNDLE_NAME = "org.opensourcephysics.resources.tools.tools"; //$NON-NLS-1$ static private ResourceBundle res = ResourceBundle.getBundle(BUNDLE_NAME); static public void setLocale(Locale locale) { res = ResourceBundle.getBundle(BUNDLE_NAME, locale); } static public String getString(String key) { try { return res.getString(key); } catch(MissingResourceException e) { return '!'+key+'!'; } } /** * The singleton shared translator tool. */ private static SnapshotTool TOOL = new SnapshotTool(); private static JFileChooser chooser; /** * Gets the shared SnapshotTool. * * @return the shared SnapshotTool */ public static SnapshotTool getTool() { if(TOOL==null) { TOOL = new SnapshotTool(); } return TOOL; } static protected void createChooser() { String[] names = javax.imageio.ImageIO.getWriterFormatNames(); String[] allNames = new String[names.length+2]; allNames[0] = "gif"; //$NON-NLS-1$ allNames[1] = "eps"; //$NON-NLS-1$ for(int i = 0; i<names.length; i++) { allNames[i+2] = names[i]; } chooser = OSPRuntime.createChooser(res.getString("SnapshotTool.ImageFiles"), allNames); //$NON-NLS-1$ } /** * Private constructor. */ private SnapshotTool() { String name = "SnapshotTool"; //$NON-NLS-1$ createChooser(); Toolbox.addTool(name, this); } /** * Sends a job to this tool and specifies a tool to reply to. * * @param job the Job * @param replyTo the tool to notify when the job is complete (may be null) * @throws RemoteException */ public void send(Job job, Tool replyTo) throws RemoteException {} /** * Saves the image produced by a component * <p> * @param filename the name of a file * @param component the component to get the image from * @return true if the file was correctly saved */ public boolean saveImage(String filename, Component component) { return saveImage(filename, component, null); } /** * Saves the image produced by a component to an output stream * <p> * @param filename the name of a file (the extension indicates the format). If null, teh user will be prompted for a name * @param component the component to get the image from * @param output An optional output stream to save to. If null the image is saved to a file * @return true if the image was correctly saved */ public boolean saveImage(String filename, Component component, java.io.OutputStream output) { return saveImage(filename, component, output, 1.0); } /** * Saves the (possibly scaled) image produced by a component to an output stream * <p> * @param filename the name of a file (the extension indicates the format). If null, the user will be prompted for a name * @param component the component to get the image from * @param output An optional output stream to save to. If null the image is saved to a file * @param scale A scale factor that resizes the image. A value of 1 uses the actual size. * @return true if the image was correctly saved */ public boolean saveImage(String filename, Component component, java.io.OutputStream output, double scale) { // Generate the image to display in the chooser (will be final if scale = 1) if(component==null) { return false; } Component originalComponent = component; if(component instanceof JFrame) { component = ((JFrame) component).getContentPane(); } else if(component instanceof JDialog) { component = ((JDialog) component).getContentPane(); } BufferedImage bi = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_3BYTE_BGR); if(component instanceof Renderable) { bi = ((Renderable) component).render(bi); } else { java.awt.Graphics g = bi.getGraphics(); component.paint(g); g.dispose(); } // we need either an output stream or a file name to save the image. if((output==null)&&(filename==null)) { // Select target file and scale JLabel label = new JLabel(); label.setIcon(new ImageIcon(bi)); /* This code is temporarily disabled because it doesn't work properly for all types of components */ JLabel labelScale = new JLabel(res.getString("SnapshotTool.Scale")); //$NON-NLS-1$ labelScale.setBorder(new javax.swing.border.EmptyBorder(0, 5, 0, 5)); JTextField scaleField = new JTextField(Double.toString(scale)); JPanel scalePanel = new JPanel(new BorderLayout()); scalePanel.add(labelScale, BorderLayout.WEST); scalePanel.add(scaleField, BorderLayout.CENTER); JPanel panel = new JPanel(new BorderLayout()); panel.add(label, BorderLayout.CENTER); panel.add(scalePanel, BorderLayout.SOUTH); chooser.setAccessory(panel); // chooser.setAccessory(label); chooser.setSelectedFile(new File("default.jpg")); // filename is null so set a default name. //$NON-NLS-1$ filename = OSPRuntime.chooseFilename(chooser); scale = Double.parseDouble(scaleField.getText()); } if(filename==null) { return false; // user hit cancel or file name not given } // Find the desired format String format = "jpg"; //$NON-NLS-1$ int index = filename.lastIndexOf('.'); if(index>=0) { format = filename.substring(index+1).toLowerCase(); } else { filename = filename+"."+format; //$NON-NLS-1$ } boolean supported = isImageFormatSupported(format); if(!(supported||"gif".equalsIgnoreCase(format)||"eps".equalsIgnoreCase(format))) { //$NON-NLS-1$ //$NON-NLS-2$ String[] message = new String[] {res.getString("SnapshotTool.FormatNotSupported"), //$NON-NLS-1$ res.getString("SnapshotTool.PreferredFormats")}; //$NON-NLS-1$ JOptionPane.showMessageDialog((JFrame) null, message, res.getString("SnapshotTool.Error"), JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$ return false; } Dimension originalSize = null; Component componentResized = null; int finalWidth = component.getWidth(), finalHeight = component.getHeight(); // Resize if scale is not 1 if(scale<=0.0) { scale = 1.0; } if(scale!=1.0) { // Take the image again, now enlarged originalSize = originalComponent.getSize(); finalWidth = (int) (originalSize.width*scale); finalHeight = (int) (originalSize.height*scale); // Do the rescaling. Also required for EPS images if(component instanceof Renderable) { bi = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_3BYTE_BGR); bi = ((Renderable) component).render(bi); component.invalidate(); } else { // resize the Swing element prior to taking the picture componentResized = originalComponent; componentResized.setSize(finalWidth, finalHeight); componentResized.validate(); bi = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_3BYTE_BGR); java.awt.Graphics g = bi.getGraphics(); component.paint(g); g.dispose(); finalWidth = component.getWidth(); // for the case of EPS files finalHeight = component.getHeight(); } } boolean result = true; try { // Do the job if(output==null) { output = new java.io.FileOutputStream(filename); } if(supported) { result = javax.imageio.ImageIO.write(bi, format, output); } else if("eps".equalsIgnoreCase(format)) { //$NON-NLS-1$ EpsGraphics2D g = new EpsGraphics2D("", output, 0, 0, finalWidth, finalHeight); //$NON-NLS-1$ g.drawImage(bi, new java.awt.geom.AffineTransform(), null); // component.paint(g); This won't work for resized Renderables g.scale(0.24, 0.24); // Set resolution to 300 dpi (0.24 = 72/300) // g.setColorDepth(EpsGraphics2D.BLACK_AND_WHITE); // Black & white g.close(); } else { // use GIFEncoder try { GIFEncoder encoder = new GIFEncoder(bi); encoder.Write(output); } catch(Exception exc) { result = false; exc.printStackTrace(); } } output.close(); } catch(Exception _exc) { _exc.printStackTrace(); result = false; } // Undo possible resizing if(componentResized!=null) { componentResized.setSize(originalSize.width, originalSize.height); componentResized.validate(); } return result; } static public boolean isImageFormatSupported(String format) { try { String[] names = javax.imageio.ImageIO.getWriterFormatNames(); for(int i = 0; i<names.length; i++) { if(names[i].equalsIgnoreCase(format)) { return true; } } } catch(Exception ex) {} return false; } // methods and classes below added by Doug Brown, 9 June 2007 /** * Copies the specified image to the system clipboard. * * @param image the image to copy */ public void copyImage(Image image) { TransferImage transfer = new TransferImage(image); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transfer, null); } /** * Returns the image on the clipboard, if any. * * @return the image, or null if none found */ public Image getClipboardImage() { Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); try { if((t!=null)&&t.isDataFlavorSupported(DataFlavor.imageFlavor)) { Image image = (Image) t.getTransferData(DataFlavor.imageFlavor); return image; } } catch(Exception ex) { ex.printStackTrace(); } return null; } /** * Copies an image of a component to the clipboard * * @param component the component to copy */ public void copyImage(Component component) { new ComponentImage(component).copyToClipboard(); } /** * Prints an image of a component * * @param component the component to print */ public void printImage(Component component) { new ComponentImage(component).print(); } /** * ComponentImage class for creating and printing images of components. */ class ComponentImage implements Printable { private BufferedImage image; Component c; /** * Constructor ComponentImage * @param comp */ public ComponentImage(Component comp) { c = comp; if(comp instanceof JFrame) { comp = ((JFrame) comp).getContentPane(); } else if(comp instanceof JDialog) { comp = ((JDialog) comp).getContentPane(); } image = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_3BYTE_BGR); if(comp instanceof Renderable) { image = ((Renderable) comp).render(image); } else { java.awt.Graphics g = image.getGraphics(); comp.paint(g); g.dispose(); } } public Image getImage() { return image; } public void copyToClipboard() { copyImage(image); } public void print() { PrinterJob printerJob = PrinterJob.getPrinterJob(); PageFormat format = new PageFormat(); java.awt.print.Book book = new java.awt.print.Book(); book.append(this, format); printerJob.setPageable(book); if(printerJob.printDialog()) { try { printerJob.print(); } catch(PrinterException pe) { // JOptionPane.showMessageDialog(c, // TrackerRes.getString("TActions.Dialog.PrintError.Message"), //$NON-NLS-1$ // TrackerRes.getString("TActions.Dialog.PrintError.Title"), //$NON-NLS-1$ // JOptionPane.ERROR_MESSAGE); } } } /** * Implements Printable. * * @param g the printer graphics * @param pageFormat the format * @param pageIndex the page number * @return status code */ public int print(Graphics g, PageFormat pageFormat, int pageIndex) { if(pageIndex>=1) { // only one page available return Printable.NO_SUCH_PAGE; } if(g==null) { return Printable.NO_SUCH_PAGE; } Graphics2D g2 = (Graphics2D) g; double scalex = pageFormat.getImageableWidth()/image.getWidth(); double scaley = pageFormat.getImageableHeight()/image.getHeight(); double scale = Math.min(scalex, scaley); scale = Math.min(scale, 1.0); // don't magnify images--only reduce if nec g2.translate((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY()); g2.scale(scale, scale); g2.drawImage(image, 0, 0, null); return Printable.PAGE_EXISTS; } } /** * Transferable class for copying images to the system clipboard. */ class TransferImage implements Transferable { private Image image; /** * Constructor TransferImage * @param image */ public TransferImage(Image image) { this.image = image; } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] {DataFlavor.imageFlavor}; } public boolean isDataFlavorSupported(DataFlavor flavor) { return DataFlavor.imageFlavor.equals(flavor); } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if(!isDataFlavorSupported(flavor)) { throw new UnsupportedFlavorException(flavor); } return image; } } // end methods and classes added by Doug Brown, 9 June 2007 } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */