import java.awt.*; import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.*; import java.io.*; import javax.swing.*; import java.util.*; /** * PrintTextDemo is a simple Swing program that * allows the user to select a text file, and then * allows him to print it at a particular font size. * It also puts a page heading on each page. */ public class PrintTextDemo extends JFrame { public static final String [] sizes={ "7", "8", "9", "10", "11", "12" }; private JTextField filepathField; private JButton browseButton; private JComboBox sizeBox; private JPanel topPanel; private JPanel bottomPanel; private JButton printButton; private JButton quitButton; private JFileChooser filer; private PageFormat pageformat; /** * Create the PrintTextDemo object. This constructor * mainly builds the GUI. */ public PrintTextDemo() { super("Print Text Demo"); Container content; JLabel sizeLabel; content = getContentPane(); content.setLayout(new BorderLayout()); topPanel = new JPanel(); bottomPanel = new JPanel(); filepathField = new JTextField(48); browseButton = new JButton("Find.."); sizeLabel = new JLabel("Font size:"); sizeBox = new JComboBox(sizes); sizeBox.setSelectedIndex(1); printButton = new JButton("Print.."); quitButton = new JButton("Quit"); topPanel.add(filepathField); topPanel.add(browseButton); topPanel.add(sizeLabel); topPanel.add(sizeBox); bottomPanel.add(printButton); bottomPanel.add(quitButton); content.add(topPanel, BorderLayout.NORTH); content.add(bottomPanel, BorderLayout.SOUTH); filer = new JFileChooser(); setHandlers(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); } /** * Utility method to pop up a dialog with an error message. */ public void popupError(String msg) { JOptionPane.showMessageDialog(this, msg, "PrintTextDemo Error", JOptionPane.ERROR_MESSAGE); } /** * Utility method to pop up a dialog with an information message. */ public void popupMessage(String msg) { JOptionPane.showMessageDialog(this, msg, "PrintTextDemo Message", JOptionPane.INFORMATION_MESSAGE); } /** * Action listener for the Print.. button; this starts * up the whole printing process: creating the PrinterJob * object, initializing our TextPageRenderer Printable * object, popping up the platform print dialog, and then * invoking the print. */ class PrintListener implements ActionListener { public void actionPerformed(ActionEvent e) { TextPageRenderer pager = null; java.util.List ls = null; String msg = null; File filepath = null; try { filepath = new File(filepathField.getText()); ls = readContentsOf(filepath); } catch (IOException ie) { msg = "Unable to handle file path '" + filepath + "'"; } if (msg == null) { int size; size = Integer.parseInt(sizeBox.getSelectedItem().toString()); PrinterJob pj; pj = PrinterJob.getPrinterJob(); pager = new TextPageRenderer(filepath.getAbsolutePath(), size, ls); pj.setJobName(filepath.getName()); pj.setPrintable(pager); if (pj.printDialog()) { try { pj.print(); } catch (PrinterException pe) { msg = "Print error: " + pe; } } } if (msg != null) { popupError(msg); } else if (pager != null) { popupMessage("Print job succeeded, " + pager.getPageCount() + " pages."); } } } /** * Action listener for the Find.. button. This pops up * the file chooser modal dialog and handles the response. */ class BrowserListener implements ActionListener { public void actionPerformed(ActionEvent e) { int ret; ret = filer.showOpenDialog(topPanel); if (ret == JFileChooser.APPROVE_OPTION) { filepathField.setText(filer.getSelectedFile().getAbsolutePath()); } } } /** * Utility method to quit the application. */ protected void quitProgram() { setVisible(false); dispose(); System.exit(0); } /** * Set the button action listeners. */ protected void setHandlers() { printButton.addActionListener(new PrintListener()); quitButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { quitProgram(); } }); browseButton.addActionListener(new BrowserListener()); } /** * Utility method to read a text file into a List. * This is rather wasteful for very large text files, * because it stores the whole thing in memory as * 2-byte characters. */ public java.util.List readContentsOf(File f) throws IOException { FileReader fr; BufferedReader br; java.util.LinkedList output; fr = new FileReader(f); br = new BufferedReader(fr); output = new java.util.LinkedList(); String line; for(line = br.readLine(); line != null; line = br.readLine()) { output.add(line); } br.close(); return output; } /** * This class handles rendering the pages. It * implements the java.awt.print.Printable interface, * which means it can be invoked from a PrinterJob. */ class TextPageRenderer implements Printable { private boolean firstcall; private int pagecount; private int fontsize; private String title; private double fontascent; private double fontheight; private Font font; private Font hfFont; private java.util.List lines; private int charsPerLine; private int linesPerPage; private AffineTransform paf; /** * This constructor stores some state that we'll need * for when the PrinterJob calls us to do printing. * Note that we cannot initialize anything about the * print rendering here, because we don't have a * Graphics object or a PageFormat object yet. So, * we just store away the information for later. * * @param ttl Page header for the print job * @param fsiz Font size for the printed page (Courier) * @param content List of lines to be printed */ public TextPageRenderer(String ttl, int fsiz, java.util.List content) { title = ttl; lines = content; fontsize = fsiz; firstcall = true; pagecount = 0; pagecount = 0; charsPerLine = 68; // default linesPerPage = 50; // default font = null; paf = null; } /** * This method renders a specified page into the given * graphic surface. The pageFormat object specifies the * size of page, based on the platform printer capabilities. * This method returns NO_SUCH_PAGE if the specified page * index is not printable, or PAGE_EXISTS otherwise. * * On the first time it is called, this method does a bunch * of computations about page capacity and such. * * Note that this method makes a number of assumptions * about the environment. First, it assumes the Graphics * object supplied from the PrinterJob is actually a * Graphics2D. This should always be true for Java 1.4 * and later. */ public int print(Graphics g,PageFormat pageFormat,int pageIndex) { Graphics2D g2 = (Graphics2D)g; if (firstcall) { firstcall = false; // check the page transform and apply paf = new AffineTransform(pageFormat.getMatrix()); g2.transform(paf); // get font font = new Font("Monospaced", Font.PLAIN, fontsize); hfFont = new Font("Monospaced", Font.BOLD, fontsize); g2.setFont(font); // compute chars per line and lines per page double cw, lh, pw, ph; LineMetrics lm; Rectangle2D rx, sb; FontRenderContext frc = g2.getFontRenderContext(); // proper call to use here would be getLineMetrics but // it always seems to fail. rx = font.getMaxCharBounds(frc); sb = font.getStringBounds("M", frc); cw = sb.getWidth(); lh = rx.getHeight() ; fontascent = sb.getHeight(); fontheight = lh; ph = pageFormat.getImageableHeight(); pw = pageFormat.getImageableWidth(); charsPerLine = (int)(pw / cw); linesPerPage = (int)(ph / lh) - 2; // adjust the lines List for wrapping adjustLines(); } else { // don't forget to apply the page transform! g2.transform(paf); } if (pageIndex > (lines.size() / linesPerPage)) { return NO_SUCH_PAGE; } // do the printing int lpos = pageIndex * linesPerPage; int lim = (pageIndex + 1) * linesPerPage; int i; String line; String heading; g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); heading = "Page " + (pageIndex+1) + " - " + title; g2.setFont(hfFont); g2.drawString(heading, 0, (int)fontascent); g2.setFont(font); for(i = 2; lpos < lim && lpos < lines.size(); i++, lpos++) { line = lines.get(lpos); g2.drawString(line, 0, (int)((i * fontheight) + fontascent)); } if (pagecount < (pageIndex + 1)) pagecount= pageIndex+1; return PAGE_EXISTS; } /** * Adjust the stored set of lines for this TextPageRenderer * based on the calculated charsPerLine. Basically, this * method splits up long lines into multiple lines, giving * the effect of wrapping them. It returns void, all its * work is done by adjusting the internal state List lines. * Note that this method does not attempt to handle * formfeed characters; it really should do so. */ private void adjustLines() { ListIterator it; String line; int pos; it = lines.listIterator(); while(it.hasNext()) { line = it.next(); if (line.length() > charsPerLine) { it.remove(); for(pos = 0; pos < line.length(); pos += charsPerLine) { if (pos+charsPerLine > line.length()) { it.add(line.substring(pos)); } else { it.add(line.substring(pos, pos+charsPerLine)); } } } } } /** * Return the largest page number that * this Printable was asked to print. */ public int getPageCount() { return pagecount; } } /** * Main method for the PrintTextDemo application. * This just creates the PrintTextDemo GUI and * causes it to pop up. */ public static void main(String [] args) { PrintTextDemo ptd; ptd = new PrintTextDemo(); ptd.setVisible(true); } }