/* * [GreetingCard.java] * * Summary: prints a greeting card on folded 8.5x11 stock. * * Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2014-06-02 initial version */ package com.mindprod.greetingcard; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.Common18; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.ImageViewer; import com.mindprod.common18.Laf; import com.mindprod.common18.Misc; import com.mindprod.common18.VersionCheck; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JSlider; import javax.swing.WindowConstants; import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Paper; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.io.IOException; import java.text.DecimalFormat; import java.util.Hashtable; import java.util.prefs.Preferences; import static java.lang.System.*; /** * prints a greeting card on folded 8.5x11 stock. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2014-06-02 initial version * @since 2014-06-02 */ public class GreetingCard extends JApplet implements Runnable { // declarations /** * true means get extra debugging output */ static final boolean DEBUGGING = true; /** * height of Applet box in pixels. Does not include surrounding frame. */ private static final int APPLET_HEIGHT = 830; /** * Width of Applet box in pixels., wide enough for a very long font name. */ private static final int APPLET_WIDTH = 620; /** * first year the program copyrighted */ private static final int FIRST_COPYRIGHT_YEAR = 2014; /** * height in pixels of preview image after aspect adjust */ private static final int IDEAL_IMAGE_HEIGHT = 3300; /** * with in pixels of preview image after aspect adjust */ private static final int IDEAL_IMAGE_WIDTH = 5100; /** * inner margin */ private static final int INNER = 5; /** * height in pixels of preview image after aspect adjust */ private static final int LANDSCAPE_PREVIEW_HEIGHT = 5100 / 10; /** * width in pixels of preview image after aspect adjust for Landscape */ private static final int LANDSCAPE_PREVIEW_WIDTH = 3300 / 10; /** * height in pixels of preview image after aspect adjust */ private static final int MIN_HEIGHT = 3300 / 15; /** * with in pixels of preview image after aspect adjust */ private static final int MIN_WIDTH = 5100 / 15; /** * outer margin */ private static final int OUTER = 20; /** * height in pixels of preview image after aspect adjust */ private static final int PREVIEW_HEIGHT = 3300 / 10; /** * width in pixels of preview image after aspect adjust */ private static final int PREVIEW_WIDTH = 5100 / 10; /** * undisplayed copyright notice * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * when program released to the public */ private static final String RELEASE_DATE = "2014-03-28"; /** * title for about box */ private static final String TITLE_STRING = "Greeting Card"; /** * program version for about box */ private static final String VERSION_STRING = "1.0"; /** * background colour, light yellomw */ private static final Color BACKGROUND_FOR_BODY = new Color( 0xf7fbd7 ); /** * instructions colour */ private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 ); /** * label foreground */ private static final Color FOREGROUND_FOR_LABEL = new Color( 0x0000b0 ); /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * warnings colour */ private static final Color FOREGROUND_FOR_WARNING = new Color( 0xdc143c/* crimson */ ); /** * used to calibrate marginWidthSlider */ private static final DecimalFormat DF1 = new DecimalFormat( "0.0" ); /** * font for labels */ private static final Font FONT_FOR_LABEL = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * for for titles and About buttons */ private static final Font FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); /** * for for title second line */ private static final Font FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); /** * filter to show just jpg, png and gif files */ private static final javax.swing.filechooser.FileFilter imagesOnly = new ImagesOnly(); /** * whether we want portrait ( wid image ) or landscape ( tall image ) */ private boolean landscape = false; /** * margin measured in 1/72 inch Adobe-points */ private int marginInPts; /** * image for card, as read from disk, before any cropping or scaling */ private BufferedImage rawBufferedImage; /** * last file chosen, null if none */ private File lastFileChosen; /** * display the image we will print */ private ImageViewer preview; /** * button to choose image */ private JButton chooseImageButton; /** * button to print page */ private JButton printButton; /** * how to use the program */ private JLabel instructions; /** * how to use the program2 */ private JLabel instructions2; /** * label marginWidthSlider */ private JLabel marginWidthLabel; /** * label step 1 with (1) */ private JLabel step1; /** * label step 2 with (2) */ private JLabel step2; /** * label step 3 with (3) */ private JLabel step3; /** * Applet Title */ private JLabel title; /** * title, second line */ private JLabel title2; /** * slider to select margin width, to nearest 1/20 inch */ private JSlider marginWidthSlider; // /declarations // methods /** * crop and scale to make the image the desired size * * @param rawBufferedImage Image * @param desiredWidth desired width * @param desiredHeight desired height * * @return cropped and scaled image (original if already right size) */ static BufferedImage cropAndScale( BufferedImage rawBufferedImage, double desiredWidth, double desiredHeight ) { final int rawWidth = rawBufferedImage.getWidth(); final int rawHeight = rawBufferedImage.getHeight(); final double rawAspectRatio = ( double ) rawWidth / ( double ) rawHeight; double desiredAspectRatio = desiredWidth / desiredHeight; final BufferedImage croppedBufferedImage; if ( rawAspectRatio > desiredAspectRatio ) { // must prune left and right slices // old image is too wide, logically chop off sides to make same shape, and scale to desired size. final int croppedWidth = ( int ) ( rawHeight * desiredAspectRatio + .5 ); croppedBufferedImage = rawBufferedImage.getSubimage( ( rawWidth - croppedWidth ) / 2, 0, croppedWidth, rawHeight ); } else if ( rawAspectRatio < desiredAspectRatio ) { // must prune top and bottom slices final int croppedHeight = ( int ) ( rawWidth / desiredAspectRatio + .5 ); croppedBufferedImage = rawBufferedImage.getSubimage( 0, ( rawHeight - croppedHeight ) / 2, rawWidth, croppedHeight ); } else { // no cropping needed croppedBufferedImage = rawBufferedImage; } if ( croppedBufferedImage.getWidth() == desiredHeight && croppedBufferedImage.getHeight() == desiredHeight ) { // already scaled nothing to do. return croppedBufferedImage; } final BufferedImage scaledBufferedImage = new BufferedImage( ( int ) desiredWidth, ( int ) desiredHeight, croppedBufferedImage.getType() ); final Graphics2D gr = scaledBufferedImage.createGraphics(); // Enable smooth scaling gr.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR ); // Scale and copy the entire cropped image into the desired image gr.drawImage( croppedBufferedImage, 0, 0, ( int ) desiredWidth, ( int ) desiredHeight, null ); // drawImage will return even before work is complete. return scaledBufferedImage; }// /method /** * allocate the components */ private void buildComponents() { title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); // title2 = new JLabel( "released:" + RELEASE_DATE + " build:" + Build.BUILD_NUMBER ); title2.setFont( FONT_FOR_TITLE2 ); title2.setForeground( FOREGROUND_FOR_TITLE ); // step1 = new JLabel(); step2 = new JLabel(); step3 = new JLabel(); step1.setIcon( new ImageIcon( GreetingCard.class.getResource( "image/1.png" ) ) ); step2.setIcon( new ImageIcon( GreetingCard.class.getResource( "image/2.png" ) ) ); step3.setIcon( new ImageIcon( GreetingCard.class.getResource( "image/3.png" ) ) ); chooseImageButton = new JButton( "choose image" ); printButton = new JButton( "print" ); printButton.setEnabled( false ); marginWidthLabel = new JLabel( "set margin width in inches" ); // marginWidthLabel.setFont( FONT_FOR_LABEL ); marginWidthLabel.setForeground( FOREGROUND_FOR_LABEL ); // allow margins 0 to 2 inches by 1/20 of an inch. marginWidthSlider = new JSlider( JSlider.HORIZONTAL, 0 /* min */, 20 * 2 /* max */, 3 * 2 /* initial */ ); marginWidthSlider.setMajorTickSpacing( 20 ); marginWidthSlider.setMinorTickSpacing( 2 ); marginWidthSlider.setPaintTicks( true ); // convert label from integers to tents of inches. @SuppressWarnings( "unchecked" ) Hashtable sliderLabels = marginWidthSlider.createStandardLabels( 10, 0 ); for ( int i = 0; i <= 20 * 2; i += 20 / 2 ) { sliderLabels.get( i ).setText( DF1.format( ( double ) i / 20 ) ); } marginWidthSlider.setLabelTable( sliderLabels ); marginWidthSlider.setPaintLabels( true ); marginWidthSlider.setPaintTrack( true ); restoreMarginFromRegistry(); // instructions = new JLabel( "Click choose image " + "to select an image for your card.", JLabel.LEFT ); instructions.setFont( FontFactory.build( "Dialog", Font.PLAIN, 12 ) ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); instructions.setBackground( BACKGROUND_FOR_BODY ); // instructions2 = new JLabel( "", JLabel.LEFT ); instructions2.setFont( FontFactory.build( "Dialog", Font.PLAIN, 12 ) ); instructions2.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); // Image dummyImage = new ImageIcon( GreetingCard.class.getResource( "image/dummy.png" ) ).getImage(); preview = new ImageViewer( dummyImage ); }// /method /** * build a menu with Look & Feel and About across the top */ private void buildMenu() { // turn on anti-alias System.setProperty( "swing.aatext", "true" ); final JMenuBar menubar = new JMenuBar(); setJMenuBar( menubar ); final JMenu fileMenu = new JMenu( "File" ); menubar.add( fileMenu ); final JMenu lafMenu = Laf.buildLookAndFeelMenu(); if ( lafMenu != null ) { menubar.add( lafMenu ); } final JMenu menuHelp = new JMenu( "Help" ); menubar.add( menuHelp ); final JMenuItem chooseItem = new JMenuItem( "Choose Image" ); fileMenu.add( chooseItem ); final JMenuItem printItem = new JMenuItem( "Print Card" ); fileMenu.add( printItem ); final JMenuItem aboutItem = new JMenuItem( "About" ); menuHelp.add( aboutItem ); chooseItem.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { chooseRawImage(); } } ); printItem.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { printCard(); } } ); aboutItem.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { // open about frame new CMPAboutJBox( Misc.getParentFrame( GreetingCard.this ), TITLE_STRING, VERSION_STRING, "Let's you print greeting cards.", "", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "GREETINGCARD", "1.8" ); } } ); }// /method /** * get the raw image from disk */ private void chooseRawImage() { chooseImageButton.setEnabled( false ); final JFileChooser fc = new JFileChooser(); fc.setFileSelectionMode( JFileChooser.FILES_ONLY ); fc.setFileFilter( imagesOnly ); // fc.addChoosableFileFilter( imagesOnly ); restoreLastFileChosenFromRegistry(); if ( lastFileChosen != null ) { // put him back to same spot was last looking. fc.setSelectedFile( lastFileChosen ); } switch ( fc.showOpenDialog( GreetingCard.this ) ) { case JFileChooser.APPROVE_OPTION: lastFileChosen = fc.getSelectedFile(); saveLastFileChosenToRegistry(); if ( DEBUGGING ) { out.println( lastFileChosen.getAbsolutePath() ); } try { rawBufferedImage = ImageIO.read( lastFileChosen ); final int width = rawBufferedImage.getWidth(); final int height = rawBufferedImage.getHeight(); landscape = width < height; final boolean good = landscape ? width >= MIN_HEIGHT && height >= MIN_WIDTH : width >= MIN_WIDTH && height >= MIN_HEIGHT; if ( !good ) { instructions.setText( "" + lastFileChosen.getAbsolutePath() + " is too small (" + width + " × " + height + "). Try " + MIN_HEIGHT + " × " + MIN_WIDTH + " or " + MIN_WIDTH + " × " + MIN_HEIGHT + " or larger." ); instructions.setForeground( FOREGROUND_FOR_WARNING ); chooseImageButton.setEnabled( true ); return; } final BufferedImage scaledBufferedImage; if ( landscape ) { scaledBufferedImage = cropAndScale( rawBufferedImage, LANDSCAPE_PREVIEW_WIDTH, LANDSCAPE_PREVIEW_HEIGHT ); } else { scaledBufferedImage = cropAndScale( rawBufferedImage, PREVIEW_WIDTH, PREVIEW_HEIGHT ); } preview.setImage( scaledBufferedImage ); preview.invalidate(); instructions.setText( "Click " + "Print to print a greeting card with this image." ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); instructions2.setText( "If you don\u2019t like the default cropping, unfortunately, " + "you will have to crop the image
" + "yourself with PaintShop Pro or similar program to " + IDEAL_IMAGE_WIDTH + " × " + IDEAL_IMAGE_HEIGHT + " pixels " + "or " + IDEAL_IMAGE_HEIGHT + " × " + IDEAL_IMAGE_WIDTH + " pixels." ); chooseImageButton.setEnabled( true ); printButton.setEnabled( true ); } catch ( IOException e ) { instructions.setText( "" + lastFileChosen.getAbsolutePath() + " can’t be read." ); instructions.setForeground( FOREGROUND_FOR_WARNING ); chooseImageButton.setEnabled( true ); printButton.setEnabled( false ); } break; case JFileChooser.CANCEL_OPTION: case JFileChooser.ERROR_OPTION: chooseImageButton.setEnabled( true ); printButton.setEnabled( false ); break; default: } }// /method /** * hook up button listeners */ private void hookListeners() { chooseImageButton.addActionListener( new ActionListener() { /** * Invoked when an choose image clicked */ public void actionPerformed( ActionEvent e ) { chooseRawImage(); } } ); printButton.addActionListener( new ActionListener() { /** * Invoked when an print clicked */ public void actionPerformed( ActionEvent e ) { printCard(); } } ); } /** * layout all the components in a GridBag * * @param contentPane where to place the components */ private void layoutComponents( Container contentPane ) { // --0---0-------------------------1 -----------2--------- // 0 title ---------------- title2-------------------- // 1 (1) (2) (3) // 2 choosebutton ~margin printbutton // 3 marginslider // 4 image ---------------------------------------------- // 5 instructions --------------------------------------- // 6 instructions2 -------------------------------------- // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( OUTER, OUTER, INNER, INNER ), 0, 0 ) ); contentPane.add( title2, new GridBagConstraints( 1, 0, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( OUTER, INNER, INNER, OUTER ), 0, 0 ) ); contentPane.add( step1, new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( INNER, OUTER, 0, INNER ), 0, 0 ) ); contentPane.add( step2, new GridBagConstraints( 1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( INNER, INNER, 0, OUTER ), 0, 0 ) ); contentPane.add( step3, new GridBagConstraints( 2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( INNER, INNER, 0, OUTER ), 0, 0 ) ); contentPane.add( chooseImageButton, new GridBagConstraints( 0, 2, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( INNER, OUTER, OUTER, INNER ), 0, 0 ) ); contentPane.add( marginWidthLabel, new GridBagConstraints( 1, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets( INNER, INNER, INNER, INNER ), 0, 0 ) ); contentPane.add( marginWidthSlider, new GridBagConstraints( 1, 3, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( INNER, INNER, INNER, INNER ), 0, 0 ) ); contentPane.add( printButton, new GridBagConstraints( 2, 2, 1, 2, 0.1, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( INNER, INNER, OUTER, OUTER ), 0, 0 ) ); contentPane.add( preview, new GridBagConstraints( 0, 4, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( INNER, OUTER, INNER, OUTER ), 0, 0 ) ); contentPane.add( instructions, new GridBagConstraints( 0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( INNER, OUTER, INNER, OUTER ), 0, 0 ) ); contentPane.add( instructions2, new GridBagConstraints( 0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets( INNER, OUTER, OUTER, OUTER ), 0, 0 ) ); }// /method /** * print the card, in four stages * * @see #run() * @see com.mindprod.greetingcard.ThePage * @see ThePage#print */ private void printCard() { printButton.setEnabled( false ); chooseImageButton.setEnabled( false ); instructions.setText( "Leave the printer property “finished” at portrait. Do not change it to landscape." ); saveMarginToRegistry(); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); new Thread( this ).start(); }// /method /** * use Preferences mechanism to track when was looking last for images. */ private void restoreLastFileChosenFromRegistry() { // get object for storing preferences Preferences userPrefs = Preferences.userRoot().node( "/com/mindprod/greetingcard" ); final String filename = userPrefs.get( "LAST_FILE_CHOSEN", null ); if ( filename == null ) { lastFileChosen = null; } else { lastFileChosen = new File( filename ); } }// /method /** * use Preferences mechanism to track when was looking last for images. */ private void restoreMarginFromRegistry() { try { // get object for storing preferences Preferences userPrefs = Preferences.userRoot().node( "/com/mindprod/greetingcard" ); final String marginStr = userPrefs.get( "MARGIN", null ); if ( marginStr != null ) { // value in 20ths of an inch. marginWidthSlider.setValue( Integer.parseInt( marginStr ) ); } } catch ( Exception e ) { /* do nothing, leave default as is */ } }// /method /** * use Preferences mechanism to track where was looking last for images. */ private void saveLastFileChosenToRegistry() { // get object for storing preferences Preferences userPrefs = Preferences.userRoot() .node( "/com/mindprod/greetingcard" ); userPrefs.put( "LAST_FILE_CHOSEN", lastFileChosen.getAbsolutePath() ); }// /method /** * use Preferences mechanism to recall previous margin. */ private void saveMarginToRegistry() { // get object for storing preferences Preferences userPrefs = Preferences.userRoot() .node( "/com/mindprod/greetingcard" ); final int marginInTwentieths = marginWidthSlider.getValue(); userPrefs.put( "MARGIN", Integer.toString( marginInTwentieths ) ); marginInPts = ( marginInTwentieths * 72 ) / 20; }// /method /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { HybridJ.fireup( new GreetingCard(), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); }// /method /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ public void destroy() { title = null; title2 = null; instructions = null; instructions2 = null; }// /method /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ @Override public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 7, 0, this.getContentPane() ) ) { // effectively abort return; } Common18.setLaf(); Container contentPane = this.getContentPane(); contentPane.setLayout( new GridBagLayout() ); contentPane.setBackground( BACKGROUND_FOR_BODY ); buildMenu(); // also initial L&F buildComponents(); hookListeners(); layoutComponents( contentPane ); this.validate(); this.setVisible( true ); }// /method /** * launch printing of the card on a separate thread */ public void run() { // Get a PrinterJob object to manage this print. final PrinterJob printerJob = PrinterJob.getPrinterJob(); final PageFormat pf = printerJob.defaultPage(); boolean landscape = GreetingCard.this.landscape; // also reverses direction of y axis, but does not ro // we don't use pf.setOrientation or printer dialog landscape. // we do everything as portrait and rotate ourselves. // The built in facilities are too clumsy. final double pageWidth = pf.getWidth(); final double pageHeight = pf.getHeight(); //pf.getImageableWidth() etc. gives 1 inch margins, pretty useless. We use our own margins. // units in postScript points 1/72 inch final double imageableWidth = pageWidth - marginInPts * 2; final double imageableHeight = pageHeight - marginInPts * 2; final Paper cardStock = new Paper(); cardStock.setImageableArea( marginInPts, marginInPts, imageableWidth, imageableHeight ); cardStock.setSize( pageWidth, pageHeight ); pf.setPaper( cardStock ); ThePage thePage = new ThePage( rawBufferedImage, cardStock, landscape ); printerJob.setPrintable( thePage ); // pop up a dialog box for the end user to fine tune the options. if ( printerJob.printDialog() ) { try { // render the component onto the printer or print queue. printerJob.print(); } catch ( PrinterException e ) { err.println( "Error printing: " + e ); } } thePage.dispose(); printButton.setEnabled( true ); chooseImageButton.setEnabled( true ); instructions.setText( "Click choose image " + "to select another image or click " + "Print to print another copy of the card." ); }// /method // /methods } /** * JFileChooser filter to just see images */ class ImagesOnly extends javax.swing.filechooser.FileFilter { /** * Whether the given file is accepted by this filter. */ public boolean accept( File f ) { String name = f.getName(); return name.endsWith( ".jpg" ) || name.endsWith( ".png" ) || name.endsWith( ".gif" ); } /** * The description of this filter. For example: "JPG and GIF Images" * * @see javax.swing.filechooser.FileView#getName */ public String getDescription() { return "Just image files, i.e. *.jpg, *.png, *.gif"; } } /** * image of the entire page we will print */ class ThePage extends JFrame implements Printable { private final BufferedImage image; private final int printImageWidth; private final int printImageHeight; private final int x; private final int y; private final boolean landscape; /** * constructor * * @param rawBufferedImage image to print, not yet scaled or cropped * @param cardstock description of paper size and margins. * Modified to use our custom margins. */ ThePage( final BufferedImage rawBufferedImage, final Paper cardstock, boolean landscape ) { super(); final int pageWidth = ( int ) cardstock.getWidth(); final int pageHeight = ( int ) cardstock.getHeight(); this.landscape = landscape; if ( landscape ) { // using the using portrait co-ordinate system, with y growing down. printImageWidth = ( int ) cardstock.getImageableHeight() / 2; printImageHeight = ( int ) cardstock.getImageableWidth(); // this point will be transformed. x = ( int ) cardstock.getImageableX(); y = ( int ) cardstock.getImageableY(); } else { this.printImageWidth = ( int ) cardstock.getImageableWidth(); this.printImageHeight = ( int ) cardstock.getImageableHeight() / 2; this.x = ( int ) cardstock.getImageableX(); this.y = pageHeight / 2; } this.image = GreetingCard.cropAndScale( rawBufferedImage, printImageWidth, printImageHeight ); this.setUndecorated( true ); this.setSize( pageWidth, pageHeight ); this.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ); Container contentPane = this.getContentPane(); contentPane.setBackground( Color.WHITE ); // setOpaque( true ); this.setVisible( false ); // don't show if ( GreetingCard.DEBUGGING ) { out.println( "frame size: " + this.getWidth() + " x " + this.getHeight() ); out.println( "image size: " + image.getWidth() + " x " + image.getHeight() ); } // we do not call print directly } // end constructor /** * print the card * * @param gr the context into which the page is drawn, high res for printer. * @param pageFormat the paper size and orientation (landscape/portrait) of the page being drawn. * @param pageIndex the zero based index of the page to be drawn. * * @return Printable.PAGE_EXISTS if the page was rendered successfully or Printable.NO_SUCH_PAGE if pageIndex * specifies a non-existent page. This is how the engine knows when you are done. */ public int print( Graphics gr, PageFormat pageFormat, int pageIndex ) { // Just like Component.paint. // In this example we ignore hints such as landscape/portrait in pageFormat // If you fail to return NO_SUCH_PAGE, you will get hundreds of pages // spewed out of your printer. if ( pageIndex > 0 ) { return Printable.NO_SUCH_PAGE; } // We will be called twice. // The co-ordinate system is 0,0 in top left. // 1 unit is 1/72 of inch. // 0,0 is the unprintable top corner final Graphics2D g2 = ( Graphics2D ) gr; if ( landscape ) { AffineTransform origTransform = g2.getTransform(); // we must glue our rotate onto existing points-to-device transform. AffineTransform af = g2.getTransform(); // set origin at center of image // old center old coords final double ocx = printImageWidth / 2 + x; final double ocy = printImageHeight / 2 + y; // new center ord coords final double ncx = printImageHeight / 2 + x; final double ncy = printImageWidth / 2 + y; // put origin to center image af.translate( ocx, ocy ); // rotate around center af.quadrantRotate( -1 ); // shift center in final position, to right and down in new coords, right and up in old af.translate( ocy - ncy, ncx - ocx ); // put origin back near upper left corner of image af.translate( -ocx, -ocy ); // hook up our rotating transform g2.setTransform( af ); // draw the image rotated, at the same scale g2.drawImage( image, x, y, printImageWidth, printImageHeight, this ); g2.setTransform( origTransform ); } else { g2.drawImage( image, x, y, printImageWidth, printImageHeight, this ); } return Printable.PAGE_EXISTS; }// /method }