/** @version 1.10 2001-08-07 @author Cay Horstmann */ import java.awt.*; import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.*; import java.util.*; import javax.print.*; import javax.print.attribute.*; import javax.swing.*; /** This program demonstrates the printing of a multi-page book. It prints a "banner", by blowing up a text string to fill the entire page vertically. The program also contains a generic print preview dialog. */ public class BookTest { public static void main(String[] args) { JFrame frame = new BookTestFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.show(); } } /** This frame has a text field for the banner text and buttons for printing, page setup, and print preview. */ class BookTestFrame extends JFrame { public BookTestFrame() { setTitle("BookTest"); setSize(WIDTH, HEIGHT); Container contentPane = getContentPane(); text = new JTextField(); contentPane.add(text, BorderLayout.NORTH); attributes = new HashPrintRequestAttributeSet(); JPanel buttonPanel = new JPanel(); JButton printButton = new JButton("Print"); buttonPanel.add(printButton); printButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { try { PrinterJob job = PrinterJob.getPrinterJob(); job.setPageable(makeBook()); if (job.printDialog(attributes)) { job.print(attributes); } } catch (PrinterException exception) { JOptionPane.showMessageDialog( BookTestFrame.this, exception); } } }); JButton pageSetupButton = new JButton("Page setup"); buttonPanel.add(pageSetupButton); pageSetupButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { PrinterJob job = PrinterJob.getPrinterJob(); pageFormat = job.pageDialog(attributes); } }); JButton printPreviewButton = new JButton("Print preview"); buttonPanel.add(printPreviewButton); printPreviewButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { PrintPreviewDialog dialog = new PrintPreviewDialog(makeBook()); dialog.show(); } }); contentPane.add(buttonPanel, BorderLayout.SOUTH); } /** Makes a book that contains a cover page and the pages for the banner. */ public Book makeBook() { if (pageFormat == null) { PrinterJob job = PrinterJob.getPrinterJob(); pageFormat = job.defaultPage(); } Book book = new Book(); String message = text.getText(); Banner banner = new Banner(message); int pageCount = banner.getPageCount((Graphics2D)getGraphics(), pageFormat); book.append(new CoverPage(message + " (" + pageCount + " pages)"), pageFormat); book.append(banner, pageFormat, pageCount); return book; } private JTextField text; private PageFormat pageFormat; private PrintRequestAttributeSet attributes; private static final int WIDTH = 300; private static final int HEIGHT = 100; } /** A banner that prints a text string on multiple pages. */ class Banner implements Printable { /** Constructs a banner @param m the message string */ public Banner(String m) { message = m; } /** Gets the page count of this section. @param g2 the graphics context @param pf the page format @return the number of pages needed */ public int getPageCount(Graphics2D g2, PageFormat pf) { if (message.equals("")) return 0; FontRenderContext context = g2.getFontRenderContext(); Font f = new Font("Serif", Font.PLAIN, 72); Rectangle2D bounds = f.getStringBounds(message, context); scale = pf.getImageableHeight() / bounds.getHeight(); double width = scale * bounds.getWidth(); int pages = (int)Math.ceil(width / pf.getImageableWidth()); return pages; } public int print(Graphics g, PageFormat pf, int page) throws PrinterException { Graphics2D g2 = (Graphics2D)g; if (page > getPageCount(g2, pf)) return Printable.NO_SUCH_PAGE; g2.translate(pf.getImageableX(), pf.getImageableY()); drawPage(g2, pf, page); return Printable.PAGE_EXISTS; } public void drawPage(Graphics2D g2, PageFormat pf, int page) { if (message.equals("")) return; page--; // account for cover page drawCropMarks(g2, pf); g2.clip(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf.getImageableHeight())); g2.translate(-page * pf.getImageableWidth(), 0); g2.scale(scale, scale); FontRenderContext context = g2.getFontRenderContext(); Font f = new Font("Serif", Font.PLAIN, 72); TextLayout layout = new TextLayout(message, f, context); AffineTransform transform = AffineTransform.getTranslateInstance(0, layout.getAscent()); Shape outline = layout.getOutline(transform); g2.draw(outline); } /** Draws 1/2" crop marks in the corners of the page. @param g2 the graphics context @param pf the page format */ public void drawCropMarks(Graphics2D g2, PageFormat pf) { final double C = 36; // crop mark length = 1/2 inch double w = pf.getImageableWidth(); double h = pf.getImageableHeight(); g2.draw(new Line2D.Double(0, 0, 0, C)); g2.draw(new Line2D.Double(0, 0, C, 0)); g2.draw(new Line2D.Double(w, 0, w, C)); g2.draw(new Line2D.Double(w, 0, w - C, 0)); g2.draw(new Line2D.Double(0, h, 0, h - C)); g2.draw(new Line2D.Double(0, h, C, h)); g2.draw(new Line2D.Double(w, h, w, h - C)); g2.draw(new Line2D.Double(w, h, w - C, h)); } private String message; private double scale; } /** This class prints a cover page with a title. */ class CoverPage implements Printable { /** Constructs a cover page. @param t the title */ public CoverPage(String t) { title = t; } public int print(Graphics g, PageFormat pf, int page) throws PrinterException { if (page >= 1) return Printable.NO_SUCH_PAGE; Graphics2D g2 = (Graphics2D)g; g2.setPaint(Color.black); g2.translate(pf.getImageableX(), pf.getImageableY()); FontRenderContext context = g2.getFontRenderContext(); Font f = g2.getFont(); TextLayout layout = new TextLayout(title, f, context); float ascent = layout.getAscent(); g2.drawString(title, 0, ascent); return Printable.PAGE_EXISTS; } private String title; } /** This class implements a generic print preview dialog. */ class PrintPreviewDialog extends JDialog { /** Constructs a print preview dialog. @param p a Printable @param pf the page format @param pages the number of pages in p */ public PrintPreviewDialog(Printable p, PageFormat pf, int pages) { Book book = new Book(); book.append(p, pf, pages); layoutUI(book); } /** Constructs a print preview dialog. @param b a Book */ public PrintPreviewDialog(Book b) { layoutUI(b); } /** Lays out the UI of the dialog. @param book the book to be previewed */ public void layoutUI(Book book) { setSize(WIDTH, HEIGHT); Container contentPane = getContentPane(); canvas = new PrintPreviewCanvas(book); contentPane.add(canvas, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); JButton nextButton = new JButton("Next"); buttonPanel.add(nextButton); nextButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { canvas.flipPage(1); } }); JButton previousButton = new JButton("Previous"); buttonPanel.add(previousButton); previousButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { canvas.flipPage(-1); } }); JButton closeButton = new JButton("Close"); buttonPanel.add(closeButton); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { setVisible(false); } }); contentPane.add(buttonPanel, BorderLayout.SOUTH); } private PrintPreviewCanvas canvas; private static final int WIDTH = 300; private static final int HEIGHT = 300; } /** The canvas for displaying the print preview. */ class PrintPreviewCanvas extends JPanel { /** Constructs a print preview canvas. @param b the book to be previewed */ public PrintPreviewCanvas(Book b) { book = b; currentPage = 0; } public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; PageFormat pageFormat = book.getPageFormat(currentPage); double xoff; // x offset of page start in window double yoff; // y offset of page start in window double scale; // scale factor to fit page in window double px = pageFormat.getWidth(); double py = pageFormat.getHeight(); double sx = getWidth() - 1; double sy = getHeight() - 1; if (px / py < sx / sy) // center horizontally { scale = sy / py; xoff = 0.5 * (sx - scale * px); yoff = 0; } else // center vertically { scale = sx / px; xoff = 0; yoff = 0.5 * (sy - scale * py); } g2.translate((float)xoff, (float)yoff); g2.scale((float)scale, (float)scale); // draw page outline (ignoring margins) Rectangle2D page = new Rectangle2D.Double(0, 0, px, py); g2.setPaint(Color.white); g2.fill(page); g2.setPaint(Color.black); g2.draw(page); Printable printable = book.getPrintable(currentPage); try { printable.print(g2, pageFormat, currentPage); } catch (PrinterException exception) { g2.draw(new Line2D.Double(0, 0, px, py)); g2.draw(new Line2D.Double(0, px, 0, py)); } } /** Flip the book by the given number of pages. @param by the number of pages to flip by. Negative values flip backwards. */ public void flipPage(int by) { int newPage = currentPage + by; if (0 <= newPage && newPage < book.getNumberOfPages()) { currentPage = newPage; repaint(); } } private Book book; private int currentPage; }