SDK 1.2 Printing API Tutorial
Introduction
The Java TM 2 Standard Development Kit, version 1.2 introduced a new printing application programming interface (API). This application interface, referred to as the SDK 1.2 Printing API or Printing API in this document, is independent of the Java 2DTM API, although it was introduced along with the powerful Java 2D graphics library and designed to image the complete set of Java 2D graphics.
This document introduces the Java 2 SDK, version 1.2 Printing API, discusses possible future capabilities of the API, and provides examples of its use.
Requirements and Restrictions
Print All of Java 2D
The breadth of Java2D graphics that can be printed through the SDK 1.2 Printing API exceeds the capabilities of most graphics subsystems, such as Windows GDI and Macintosh Quickdraw. Java 2D also exceeds the power of existing printer control languages, such as PCL 5 and PostScript. Nonetheless, one of the requirements for the new Printing API was that it print the full set of Java 2D grqaphics, even though the host and printer capabilities are overmatched by Java2D. This requirement meant that the Printing API, in some situations, would need to rasterize Java 2D graphics on the host computer.
No Spool Format Available
To rasterize print jobs on a host computer, a print subsystem typically spools graphics primitives to disk. As each band of the raster needs to be sent to the printer, the spool file is replayed in order to render the band. Neither the AWT nor the Java 2D graphics APIs provide a spool format for their primitives. This lack of an existing spool format combined with the tight development schedule of SDK 1.2 meant that a non-spooling solution be implemented to support host rasterized print jobs. The solution implemented in the SDK 1.2 Printing API has the print subsystem call back into the application to rasterize a band
What the Printing API Is Not
A complete printing system consists of several layers, extending from the printing application down to the printer, as illustrated in Figure 1. The application interacts with the printing system for two reasons: to target a printer and to generate printed pages.
Implementation Notes
SDK 1.2
The PrinterJob Class
PrinterJob printerJob = PrinterJob.getPrinterJob();
The Book Class
Book book = new Book();
book.append(new PrintBlank(), new PageFormat());
printerJob.setPageable(book);
The Print Dialog
boolean doPrint = printerJob.printDialog();
if (doPrint) {// The user confirmed that printing should proceed.
}
Printing
printerJob.print();
A Simple Example: PrintBlank
import java.awt.*; import java.awt.print.*; /** * This example shows how to use the PrinterJob * and Book classes. */
public class PrintBlank implements Printable {
/** * Print a single, blank page. */ static public void main(String args[]) {/* Get the representation of the current printer and * the current print job. */ PrinterJob printerJob = PrinterJob.getPrinterJob();/* Build a book containing pairs of page painters (Printables) * and PageFormats. This example has a single page. */ Book book = new Book(); book.append(new PrintBlank(), new PageFormat());
/* Set the object to be printed (the Book) into the PrinterJob. * Doing this before bringing up the print dialog allows the * print dialog to correctly display the page range to be printed * and to dissallow any print settings not appropriate for the * pages to be printed. */ printerJob.setPageable(book);
/* Show the print dialog to the user. This is an optional step * and need not be done if the application wants to perform * 'quiet' printing. If the user cancels the print dialog then false * is returned. If true is returned we go ahead and print. */ boolean doPrint = printerJob.printDialog(); if (doPrint) {
try {printerJob.print();} catch (PrinterException exception) {System.err.println("Printing error: " + exception);}}}
/** * Print a blank page. */ public int print(Graphics g, PageFormat format, int pageIndex) {
/* Do the page drawing here. This example does not do any * drawing and therefore blank pages are generated: * "This Page Intentionally Left Blank" */ return Printable.PAGE_EXISTS; }
}
The Printable Interface
import java.awt.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.*; import java.text.*;
/** * The PrintText application expands on the * PrintExample application in that it images * text on to the single page printed. */ public class PrintText implements Printable {
/** * The text to be printed. */ private static final String mText = "Four score and seven years ago our fathers brought forth on this " + "continent a new nation, conceived in liberty and dedicated to the " + "proposition that all men are created equal. Now we are engaged in " + "a great civil war, testing whether that nation or any nation so " + "conceived and so dedicated can long endure. We are met on a great " + "battlefield of that war. We have come to dedicate a portion of " + "that field as a final resting-place for those who here gave their " + "lives that that nation might live. It is altogether fitting and " + "proper that we should do this. But in a larger sense, we cannot " + "dedicate, we cannot consecrate, we cannot hallow this ground." + "The brave men, living and dead who struggled here have consecrated " + "it far above our poor power to add or detract. The world will " + "little note nor long remember what we say here, but it can never " + "forget what they did here. It is for us the living rather to be " + "dedicated here to the unfinished work which they who fought here " + "have thus far so nobly advanced. It is rather for us to be here " + "dedicated to the great task remaining before us--that from these " + "honored dead we take increased devotion to that cause for which " + "they gave the last full measure of devotion--that we here highly " + "resolve that these dead shall not have died in vain, that this " + "nation under God shall have a new birth of freedom, and that " + "government of the people, by the people, for the people shall " + "not perish from the earth.";
/** * Our text in a form for which we can obtain a * AttributedCharacterIterator. */ private static final AttributedString mStyledText = new AttributedString(mText);
/** * Print a single page containing some sample text. */ static public void main(String args[]) {
/* Get the representation of the current printer and * the current print job. */ PrinterJob printerJob = PrinterJob.getPrinterJob();
/* Build a book containing pairs of page painters (Printables) * and PageFormats. This example has a single page containing * text. */ Book book = new Book(); book.append(new PrintText(), new PageFormat());
/* Set the object to be printed (the Book) into the PrinterJob. * Doing this before bringing up the print dialog allows the * print dialog to correctly display the page range to be printed * and to dissallow any print settings not appropriate for the * pages to be printed. */ printerJob.setPageable(book);
/* Show the print dialog to the user. This is an optional step * and need not be done if the application wants to perform * 'quiet' printing. If the user cancels the print dialog then false * is returned. If true is returned we go ahead and print. */ boolean doPrint = printerJob.printDialog(); if (doPrint) {
try {printerJob.print();} catch (PrinterException exception) {System.err.println("Printing error: " + exception);}}} /** * Print a page of text. */ public int print(Graphics g, PageFormat format, int pageIndex) {/* We'll assume that Jav2D is available. */ Graphics2D g2d = (Graphics2D) g;/* Move the origin from the corner of the Paper to the corner * of the imageable area. */ g2d.translate(format.getImageableX(), format.getImageableY());/* Set the text color. */ g2d.setPaint(Color.black);
/* Use a LineBreakMeasurer instance to break our text into * lines that fit the imageable area of the page. */ Point2D.Float pen = new Point2D.Float(); AttributedCharacterIterator charIterator = mStyledText.getIterator(); LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, g2d.getFontRenderContext()); float wrappingWidth = (float) format.getImageableWidth();
while (measurer.getPosition() < charIterator.getEndIndex()) {TextLayout layout = measurer.nextLayout(wrappingWidth); pen.y += layout.getAscent(); float dx = layout.isLeftToRight()? 0 : (wrappingWidth - layout.getAdvance());layout.draw(g2d, pen.x + dx, pen.y); pen.y += layout.getDescent() + layout.getLeading();} return Printable.PAGE_EXISTS;}
The Page Format Dialog
/** * Print a single page containing some sample text. */ static public void main(String args[]) {
/* Get the representation of the current printer and * the current print job. */ PrinterJob printerJob = PrinterJob.getPrinterJob(); /* Let the user choose a paper size and orientation. */ PageFormat format = new PageFormat(); format = printerJob.pageDialog(format); /* Build a book containing pairs of page painters (Printables) * and PageFormats. This example has a single page containing * text. */ Book book = new Book(); book.append(new PageSetupText(), format); /* Set the object to be printed (the Book) into the PrinterJob. * Doing this before bringing up the print dialog allows the * print dialog to correctly display the page range to be printed * and to dissallow any print settings not appropriate for the * pages to be printed. */ printerJob.setPageable(book); /* Show the print dialog to the user. This is an optional step * and need not be done if the application wants to perform * 'quiet' printing. If the user cancels the print dialog then false * is returned. If true is returned we go ahead and print. */ boolean doPrint = printerJob.printDialog(); if (doPrint) {
try {printerJob.print();} catch (PrinterException exception) {System.err.println("Printing error: " + exception);}}}
The PageFormat Class
Figure 3: Portrait PageFormat Dimensions and Coordinates |
Figure 4: Landscape PageFormat Dimensions and Coordinates |
import java.awt.*; import java.awt.font.*; import java.awt.print.*; import java.text.*; import java.util.*; public class FooterFormat extends PageFormat implements Printable {
/** * The font we use for the footer. */ private static final Font mFooterFont = new Font("Serif", Font.ITALIC, 10); /** * The amount of space at the bottom of the imageable area that we * reserve for the footer. */ private static final float mFooterHeight = (float) (0.25 * 72); /** * The format for the date string shown in the footer. */ private static final DateFormat mDateFormat = new SimpleDateFormat(); /** * A formatted string describing when this instance was * created. */ private String mDateStr = mDateFormat.format(new Date()).toString(); /** * Tell the caller that the imageable area of the paper is shorter * than it actually is. We use the extra room at the bottom of the * page for our footer text. */ public double getImageableHeight() {
double imageableHeight = super.getImageableHeight() - mFooterHeight; if (imageableHeight < 0) imageableHeight = 0; return imageableHeight;} /** * Draws the footer text which has the following format: * <date> */ public int print(Graphics g, PageFormat format, int pageIndex) {
/* Make a copy of the passed in Graphics instance so * that we do not upset the caller's current Graphics * settings such as the current color and font. */ Graphics2D g2d = (Graphics2D) g.create(); g2d.setPaint(Color.black); g2d.setFont(mFooterFont); LineMetrics metrics = mFooterFont.getLineMetrics(mDateStr, g2d.getFontRenderContext()); /* We will draw the footer at the bottom of the imageable * area. We subtract off the font's descent so that the bottoms * of descenders remain visable. */ float y = (float) (super.getImageableY() + super.getImageableHeight()- metrics.getDescent() - metrics.getLeading()); // Cast to an int because of printing bug in drawString(String, float, float)! g2d.drawString(mDateStr, (int) super.getImageableX(), (int)y); g2d.dispose(); return Printable.PAGE_EXISTS;
}}
import java.awt.*; import java.awt.font.*; import java.awt.geom.*; import java.awt.print.*; import java.text.*; /** * The TextWithFooter application uses the * page setup and print dialogs to print * a hunk of text. A FooterFormat class * is used to print a footer on the page. */ public class TextWithFooter implements Printable {
/** * The text to be printed. */ private static final String mText = "Four score and seven years ago our fathers brought forth on this " + "continent a new nation, conceived in liberty and dedicated to the " + "proposition that all men are created equal. Now we are engaged in " + "a great civil war, testing whether that nation or any nation so " + "conceived and so dedicated can long endure. We are met on a great " + "battlefield of that war. We have come to dedicate a portion of " + "that field as a final resting-place for those who here gave their " + "lives that that nation might live. It is altogether fitting and " + "proper that we should do this. But in a larger sense, we cannot " + "dedicate, we cannot consecrate, we cannot hallow this ground." + "The brave men, living and dead who struggled here have consecrated " + "it far above our poor power to add or detract. The world will " + "little note nor long remember what we say here, but it can never " + "forget what they did here. It is for us the living rather to be " + "dedicated here to the unfinished work which they who fought here " + "have thus far so nobly advanced. It is rather for us to be here " + "dedicated to the great task remaining before us--that from these " + "honored dead we take increased devotion to that cause for which " + "they gave the last full measure of devotion--that we here highly " + "resolve that these dead shall not have died in vain, that this " + "nation under God shall have a new birth of freedom, and that " + "government of the people, by the people, for the people shall " + "not perish from the earth."; /** * Our text in a form for which we can obtain a * AttributedCharacterIterator. / private static final AttributedString mStyledText = new AttributedString(mText); /** * Print a single page containing some sample text. */ static public void main(String args[]) {
/* Get the representation of the current printer and * the current print job. */ PrinterJob printerJob = PrinterJob.getPrinterJob(); /* Use a PageFormat that also prints a footer. */ PageFormat format = new FooterFormat(); /* Let the user choose a paper size and orientation. */ format = printerJob.pageDialog(format); /* Build a book containing pairs of page painters (Printables) * and PageFormats. This example has a single page containing * text. */ Book book = new Book(); book.append(new TextWithFooter(), format); /* Set the object to be printed (the Book) into the PrinterJob. * Doing this before bringing up the print dialog allows the * print dialog to correctly display the page range to be printed * and to dissallow any print settings not appropriate for the * pages to be printed. */ printerJob.setPageable(book); /* Show the print dialog to the user. This is an optional step * and need not be done if the application wants to perform * 'quiet' printing. If the user cancels the print dialog then false * is returned. If true is returned we go ahead and print. */ boolean doPrint = printerJob.printDialog(); if (doPrint) {
try {printerJob.print();} catch (PrinterException exception) {System.err.println("Printing error: " + exception);}}
} /** * Print a page of text. */ public int print(Graphics g, PageFormat format, int pageIndex) throws PrinterException { /* We'll assume that Jav2D is available. Create a copy * of it so that we can pass the original Graphics * instance to the PageFormat instance. */ Graphics2D g2d = (Graphics2D) g.create(); /* Move the origin from the corner of the Paper to the corner * of the imageable area. */ g2d.translate(format.getImageableX(), format.getImageableY()); /* Set the text color. */ g2d.setPaint(Color.black); /* Use a LineBreakMeasurer instance to break our text into * lines that fit the imageable area of the page. */ Point2D.Float pen = new Point2D.Float(); AttributedCharacterIterator charIterator = mStyledText.getIterator(); LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, g2d.getFontRenderContext()); float wrappingWidth = (float) format.getImageableWidth(); while (measurer.getPosition() < charIterator.getEndIndex()) {
TextLayout layout = measurer.nextLayout(wrappingWidth); pen.y += layout.getAscent(); float dx = layout.isLeftToRight() ? 0 : (wrappingWidth - layout.getAdvance()); layout.draw(g2d, pen.x + dx, pen.y); pen.y += layout.getDescent() + layout.getLeading();} g2d.dispose(); g2d = null;/* Calling the PageFormat is not part of the printing API,
* but it is a useful convention. In this example PageFormat
* does not implement Printable and so it is not invoked here.
* In later examples, PageFormat will implement Printable.
*/
try {Printable formatPainter = (Printable) format; formatPainter.print(g, format, pageIndex);/* Nothing to do here. The PageFormat has nothing to print. */ } catch (ClassCastException exception) { } return Printable.PAGE_EXISTS;}
The PrinterGraphics Interface
The Pageable Arial
import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.print.Pageable; import java.awt.print.PageFormat import java.awt.print.Printable; import java.awt.print.PrinterException; /** * A simple Pageable class that can * split a large drawing canvas over multiple * pages. * * The pages in a canvas are laid out on * pages going left to right and then top * to bottom. */ public class Vista implements Pageable {
private int mNumPagesX; private int mNumPagesY; private int mNumPages; private Printable mPainter; private PageFormat mFormat; /** * Create a java.awt.Pageable that will print * a canvas over as many pages as are needed. * A Vista can be passed to PrinterJob.setPageable. * * @param width The width, in 1/72nds of an inch, * of the vist's canvas. * * @param height The height, in 1/72nds of an inch, * of the vista's canvas. * * @param painter The object that will drawn the contents * of the canvas. * * @param format The description of the pages on to which * the canvas will be drawn. */ public Vista(float width, float height, Printable painter, PageFormat format) {
setPrintable(painter); setPageFormat(format); setSize(width, height);
} /** * Create a vista over a canvas whose width and height * are zero and whose Printable and PageFormat are null. */ protected Vista() { } /** * Set the object responsible for drawing the canvas. */ protected void setPrintable(Printable painter) {
mPainter = painter;
} /** * Set the page format for the pages over which the * canvas will be drawn. */ protected void setPageFormat(PageFormat pageFormat) {
mFormat = pageFormat;
} /** * Set the size of the canvas to be drawn. * * @param width The width, in 1/72nds of an inch, of * the vist's canvas. * * @param height The height, in 1/72nds of an inch, of * the vista's canvas. */ protected void setSize(float width, float height) {
mNumPagesX = (int) ((width + mFormat.getImageableWidth() - 1)/ mFormat.getImageableWidth()); mNumPagesY = (int) ((height + mFormat.getImageableHeight() - 1)/ mFormat.getImageableHeight()); mNumPages = mNumPagesX * mNumPagesY;} /** * Returns the number of pages over which the canvas * will be drawn. */ public int getNumberOfPages() {return mNumPages;} protected PageFormat getPageFormat() {return mFormat;
} /** * Returns the PageFormat of the page specified by * pageIndex. For a Vista the PageFormat * is the same for all pages. * * @param pageIndex the zero based index of the page whose * PageFormat is being requested * @return the PageFormat describing the size and * orientation. * @exception IndexOutOfBoundsException * the Pageable does not contain the requested * page. */ public PageFormat getPageFormat(int pageIndex) throws IndexOutOfBoundsException {
if (pageIndex >= mNumPages) {throw new IndexOutOfBoundsException();} return getPageFormat();
} /** * Returns the <code>Printable</code> instance responsible for * rendering the page specified by <code>pageIndex</code>. * In a Vista, all of the pages are drawn with the same * Printable. This method however creates * a Printable which calls the canvas's * Printable. This new Printable * is responsible for translating the coordinate system * so that the desired part of the canvas hits the page. * * The Vista's pages cover the canvas by going left to * right and then top to bottom. In order to change this * behavior, override this method. * * @param pageIndex the zero based index of the page whose * Printable is being requested * @return the Printable that renders the page. * @exception IndexOutOfBoundsException * the Pageable does not contain the requested * page. */ public Printable getPrintable(int pageIndex) throws IndexOutOfBoundsException {
if (pageIndex >= mNumPages) {throw new IndexOutOfBoundsException();} double originX = (pageIndex % mNumPagesX) * mFormat.getImageableWidth(); double originY = (pageIndex / mNumPagesX) * mFormat.getImageableHeight(); Point2D.Double origin = new Point2D.Double(originX, originY); return new TranslatedPrintable(mPainter, origin);
} /** * This inner class's sole responsibility is to translate * the coordinate system before invoking a canvas's * painter. The coordinate system is translated in order * to get the desired portion of a canvas to line up with * the top of a page. */ public static final class TranslatedPrintable implements Printable {
/** * The object that will draw the canvas. */ private Printable mPainter; /** * The upper-left corner of the part of the canvas * that will be displayed on this page. This corner * is lined up with the upper-left of the imageable * area of the page. */ private Point2D mOrigin; /** * Create a new Printable that will translate * the drawing done by painter on to the * imageable area of a page. * * @param painter The object responsible for drawing * the canvas * * @param origin The point in the canvas that will be * mapped to the upper-left corner of * the page's imageable area. */ public TranslatedPrintable(Printable painter, Point2D origin) {
mPainter = painter; mOrigin = origin;
} /** * Prints the page at the specified index into the specified * {@link Graphics} context in the specified * format. A PrinterJob calls the * Printableinterface to request that a page be * rendered into the context specified by * graphics. The format of the page to be drawn is * specified by pageFormat. The zero based index * of the requested page is specified by pageIndex. * If the requested page does not exist then this method returns * NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned. * The Graphics class or subclass implements the * {@link PrinterGraphics} interface to provide additional * information. If the Printable object * aborts the print job then it throws a {@link PrinterException}. * @param graphics the context into which the page is drawn * @param pageFormat the size and orientation of the page being drawn * @param pageIndex the zero based index of the page to be drawn * @return PAGE_EXISTS if the page is rendered successfully * or NO_SUCH_PAGE if pageIndex specifies a * non-existent page. * @exception java.awt.print.PrinterException * thrown when the print job is terminated. */ public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
Graphics2D g2 = (Graphics2D) graphics; g2.translate(-mOrigin.getX(), -mOrigin.getY()); mPainter.print(g2, pageFormat, 1); return PAGE_EXISTS;}}}
import java.awt.*; import java.awt.print.*; import javax.swing.*; public class JComponentVista extends Vista implements Printable {
private static final boolean SYMMETRIC_SCALING = true; private static final boolean ASYMMETRIC_SCALING = false; private double mScaleX; private double mScaleY; /** * The Swing component to print. */ private JComponent mComponent; /** * Create a Pageable that can print a * Swing JComponent over multiple pages. * * @param c The swing JComponent to be printed. * * @param format The size of the pages over which * the componenent will be printed. */ public JComponentVista(JComponent c, PageFormat format) {
setPageFormat(format); setPrintable(this); setComponent(c); /* Tell the Vista we subclassed the size of the canvas. */ Rectangle componentBounds = c.getBounds(null); setSize(componentBounds.width, componentBounds.height); setScale(1, 1);
} protected void setComponent(JComponent c) {mComponent = c;} protected void setScale(double scaleX, double scaleY) {mScaleX = scaleX; mScaleY = scaleY;} public void scaleToFitX() {PageFormat format = getPageFormat(); Rectangle componentBounds = mComponent.getBounds(null); double scaleX = format.getImageableWidth() /componentBounds.width; double scaleY = scaleX; if (scaleX < 1) {setSize( (float) format.getImageableWidth(), (float) (componentBounds.height * scaleY)); setScale(scaleX, scaleY);}}public void scaleToFitY() {PageFormat format = getPageFormat(); Rectangle componentBounds = mComponent.getBounds(null); double scaleY = format.getImageableHeight() /componentBounds.height; double scaleX = scaleY; if (scaleY < 1) {setSize( (float) (componentBounds.width * scaleX),(float) format.getImageableHeight()); setScale(scaleX, scaleY);}}public void scaleToFit(boolean useSymmetricScaling) {PageFormat format = getPageFormat(); Rectangle componentBounds = mComponent.getBounds(null); double scaleX = format.getImageableWidth() /componentBounds.width; double scaleY = format.getImageableHeight() /componentBounds.height; System.out.println("Scale: " + scaleX + " " + scaleY); if (scaleX < 1 || scaleY < 1) {if (useSymmetricScaling) {if (scaleX < scaleY) {scaleY = scaleX;} else {scaleX = scaleY;}} setSize( (float) (componentBounds.width * scaleX), (float) (componentBounds.height * scaleY) ); setScale(scaleX, scaleY);)} public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
Graphics2D g2 = (Graphics2D) graphics; g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); Rectangle componentBounds = mComponent.getBounds(null); g2.translate(-componentBounds.x, -componentBounds.y); g2.scale(mScaleX, mScaleY); boolean wasBuffered = mComponent.isDoubleBuffered(); mComponent.paint(g2); mComponent.setDoubleBuffered(wasBuffered); return PAGE_EXISTS;
}
)
import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import java.awt.Window;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;/**
* Uses JFrame to create a simple browser with printing.
* User passes String URL (e.g., http://www.rbi.com)
* to set opening location. User then may follow
* hyperlinks or type new preferred location into
* provided JTextField.
*
* Scaling options are demonstrated in this
* example. Scaling options may be set from a
* submenu in the File menu or by specified KeyStroke.
* A second menu track nn websites, which user can
* reselect as destination using mouse.
*/
public class JBrowser extends JFrame {
private static final int kNumSites = 20; //number of sites listed in JMenu "Last 20"}
private static final int kDefaultX = 640;
private static final int kDefaultY = 480;
private static final int prefScale = 0;private static final String kScale2Label = "2X Scale";
private static final String kScaleFitLabel = "Scale to Fit";
private static final String kScaleHalfLabel = "1/2 Scale";
private static final String kScaleOffLabel = "Scaling Off";
private static final String kScaleXLabel = "Scale by Width";
private static final String kScaleYLabel = "Scale by Length";private JEditorPane mainPane;
private String path;
private JButton goButton = new JButton("Go");
private JComponentVista vista;
private JMenu fileMenu = new JMenu("File", true);
private JMenu prefMenu = new JMenu("Print Preferences", true);
private JMenu siteMenu = new JMenu("Last 20", true);
private JRadioButtonMenuItem scale2RadioBut = new JRadioButtonMenuItem(kScale2Label);
private JRadioButtonMenuItem scaleFitRadioBut = new JRadioButtonMenuItem(kScaleFitLabel);
private JRadioButtonMenuItem scaleHalfRadioBut = new JRadioButtonMenuItem(kScaleHalfLabel);
private JRadioButtonMenuItem scaleOffRadioBut = new JRadioButtonMenuItem(kScaleOffLabel, true);
private JRadioButtonMenuItem scaleXRadioBut = new JRadioButtonMenuItem(kScaleXLabel);
private JRadioButtonMenuItem scaleYRadioBut = new JRadioButtonMenuItem(kScaleYLabel);
private JTextField pathField = new JTextField(30);
private Vector siteMIVector = new Vector();public static void main(String[] args) {
new JBrowser(args[0]);}public JBrowser(String url) {
super("JBrowser HTML Printing Demo");}
path = url;
addSite(path);try {
mainPane = new JEditorPane(path);} catch (IOException ex) {ex.printStackTrace(System.err);}
System.exit(1);JMenuBar menuBar = new JMenuBar();
JPanel navPanel = new JPanel();
JMenuItem printMI = new JMenuItem("Print");
JMenuItem exitMI = new JMenuItem("Exit");printMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK));
exitMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.CTRL_MASK));scale2RadioBut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK));
scaleFitRadioBut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK));
scaleHalfRadioBut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, Event.CTRL_MASK));
scaleOffRadioBut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, Event.CTRL_MASK));
scaleXRadioBut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, Event.CTRL_MASK));
scaleYRadioBut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.CTRL_MASK));printMI.addActionListener(new printMIListener());
exitMI.addActionListener(new exitMIListener());scaleXRadioBut.addActionListener(new scaleXListener());
scaleYRadioBut.addActionListener(new scaleYListener());
scaleFitRadioBut.addActionListener(new scaleFitListener());
scaleHalfRadioBut.addActionListener(new scaleHalfListener());scale2RadioBut.addActionListener(new scale2Listener());
pathField.addActionListener(new pathFieldListener());
pathField.setText(path);
goButton.addActionListener(new pathFieldListener());ButtonGroup scaleSetGroup = new ButtonGroup();
scaleSetGroup.add(scale2RadioBut);
scaleSetGroup.add(scaleFitRadioBut);
scaleSetGroup.add(scaleHalfRadioBut);
scaleSetGroup.add(scaleOffRadioBut);
scaleSetGroup.add(scaleXRadioBut);
scaleSetGroup.add(scaleYRadioBut);prefMenu.add(scaleXRadioBut);
prefMenu.add(scaleYRadioBut);
prefMenu.add(scaleFitRadioBut);
prefMenu.add(scaleHalfRadioBut);
prefMenu.add(scale2RadioBut);
prefMenu.addSeparator();
prefMenu.add(scaleOffRadioBut);fileMenu.add(prefMenu);
fileMenu.add(printMI);
fileMenu.addSeparator();
fileMenu.add(exitMI);menuBar.add(fileMenu);
menuBar.add(siteMenu);
menuBar.add(pathField);
menuBar.add(goButton);
mainPane.setEditable(false);
mainPane.addHyperlinkListener(new linkListener());vista = new JComponentVista(mainPane, new PageFormat());
addWindowListener(new BasicWindowMonitor());
setContentPane(new JScrollPane(mainPane));
setVisible(true);setJMenuBar(menuBar);
setSize(kDefaultX, kDefaultY);
setVisible(true);/*
* addSite method takes the String url and adds it to the
* Vector siteMIVector and the JMenu siteMenu.
*/
public void addSite(String url) {boolean beenThere = false;/*
* Cycle through the contents of the siteMenu, comparing
* their labels to the string to determine if there is
* redundancy.
*/
for(int i=0; i<siteMenu.getItemCount() && !beenThere; i++) {JMenuItem site = siteMenu.getItem(i);/*
* The String url, is compared to the labels of the
* JMenuItems in already stored in siteMIVector. If
* the string matches an existing label, the older
* redundant element, at i, is removed. The new JMenuItem
* site is inserted in the Vector at 0. The updateMenu
* method is called to update the "Last nn" menu accordingly
* and the "beenThere" boolean trigger is set TRUE.
*/
if (site.getText().equals(url)) {siteMIVector.removeElementAt(i);
siteMIVector.insertElementAt(site, 0);
updateMenu(siteMenu);
beenThere = true;}}/*}
* If the new JMenuItem site has a unique string, then the
* addSite method handles it as follows.
*/
if (!beenThere) {/*}
* If the "Last nn" menu has reached kNumSites capacity,
* the oldest JMenuItem is removed from the vector, enabling
* storage for the new menu item and maintaining the specified
* capacity of the "Last nn" menu.
*/
if (siteMenu.getItemCount() >= kNumSites){siteMIVector.removeElementAt(siteMIVector.size()-1);}/*
* A new JMenuItem is created and a siteMenuListener added.
* It is added to the vector then the menu is updated.
*/
JMenuItem site = new JMenuItem(url);
site.addActionListener(new siteMenuListener(url));
siteMIVector.insertElementAt(site, 0);
System.out.println("\n Connected to "+ url);
updateMenu(siteMenu);public void updateMenu(JMenu menu) {
menu.removeAll();}
for(int i=0; i<siteMIVector.size(); i++) {JMenuItem mi = (JMenuItem)siteMIVector.elementAt(i);}
menu.add(mi);/*
*
* The ActionListener methods
*
*/
public class exitMIListener implements ActionListener {public void actionPerformed(ActionEvent evt) {System.out.println("\n Killing JBrowser...");
System.out.println(" ...AHHHHHHHHHhhhhhhh...ya got me...ugh");
System.exit(0);}}public class linkListener implements HyperlinkListener {
public void hyperlinkUpdate(HyperlinkEvent ev) {}try {}if (ev.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {} catch (IOException ex) {mainPane.setPage(ev.getURL());}
path = ev.getURL().toString();
pathField.setText(path);
addSite(path);ex.printStackTrace(System.err);}public class pathFieldListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {}System.out.println("\n Switching from "+path+" to "+pathField.getText()+".");}
path = pathField.getText();
try {mainPane.setPage(path);} catch (IOException ex){ex.printStackTrace(System.err);}if (!path.equals("")) {
addSite(path);}public class printMIListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPageable(vista);
try {if (pj.printDialog()) {} catch (PrinterException e) {pj.print();}System.out.println(e);}}}public class scale2Listener implements ActionListener {
public void actionPerformed(ActionEvent evt) {}vista = new JComponentVista(mainPane, new PageFormat());}
vista.setScale(2.0, 2.0);public class scaleFitListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {}vista = new JComponentVista(mainPane, new PageFormat());}
vista.scaleToFit(false)public class scaleHalfListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {}vista = new JComponentVista(mainPane, new PageFormat());}
vista.setScale(0.5, 0.5);public class scaleOffListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {vista = new JComponentVista(mainPane, new PageFormat());}}public class scaleXListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {}vista = new JComponentVista(mainPane, new PageFormat());}
vista.scaleToFitX();public class scaleYListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {}vista = new JComponentVista(mainPane, new PageFormat());}
vista.scaleToFitY();public class siteMenuListener implements ActionListener {
private String site;}public siteMenuListener(String url) {
site = url;}public void actionPerformed(ActionEvent evt) {
System.out.println("\n Switching from "+path+" to "+site+".");}
path = site;
try {mainPane.setPage(path);} catch (IOException ex){ex.printStackTrace(System.err);}if (!path.equals("")) {
addSite(path);}pathField.setText(path);