/*  J2PrinterWorks Test Application
    (C) Copyright 2009
    Wildcrest Associates
    http://www.wildcrest.com
This source code may be freely used, modified, incorporated, and
distributed without restriction as part of any software that uses
J2PrinterWorks by Wildcrest Associates.
*/

import com.wildcrest.j2printerworks.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.math.*;

public class J2PrinterWorksTestApplication {

    static J2Printer printer = new J2Printer(""); // make J2Printer instance
    // or to use new JDK 1.4 features: static J2Printer14 printer = new J2Printer14("");
    // After purchase, use J2Printer("your-serial-number") or J2Printer14("your-serial-number")

    static public boolean onScreen;
    static public void setOnScreen(boolean val) { onScreen = val; }

    static JFrame frame;
    static JLabel status;


    static public void main(String args[]) {

// JDK 1.2.x warning: HTML JTextPane portions don't print because of Java serialization bugs

// Ask whether to display components on screen
        JFrame jf = new JFrame();
        final JDialog opt = new JDialog(jf,"J2PrinterWorks Test Application", true);
        JTextArea jta = new JTextArea("This application prints a document that contains a number of\n"
            + "JTextPane, JTable, JTree, JList, JPanel, and other Components.\n\n"
            + "Do you also want these displayed on the screen?\n");
        jta.setBorder(new EmptyBorder(10,10,10,10));
        jta.setFont(new Font("SanSerif",Font.BOLD,12));
        jta.setOpaque(false);
        jta.setEditable(false);
        opt.getContentPane().add(jta, "Center");
        JButton yes = new JButton("Display");
        JButton no = new JButton("Don't display");
        JPanel jp = new JPanel(); jp.add(yes); jp.add(no);
        opt.getContentPane().add(jp, "South");
        yes.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { setOnScreen(true); opt.setVisible(false); }});
        no.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { setOnScreen(false); opt.setVisible(false); }});
        opt.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) { System.exit(0); }});
        opt.pack();
        Dimension sDim = Toolkit.getDefaultToolkit().getScreenSize();
        Rectangle fDim = opt.getBounds();
        opt.setLocation( (sDim.width - fDim.width) / 2, (sDim.height - fDim.height) / 2);
        opt.toFront();
        jf.toFront();
        opt.setVisible(true);

// printer settings
        printer.setLeftMargin(.25); printer.setRightMargin(.25); printer.setTopMargin(.25); printer.setBottomMargin(.25);
        printer.setPrintPreviewScale(1.0);       // usually 0.5 but this makes things easier to see
//      printer.setParam("your serial number");  // include after you purchase J2PrinterWorks product


// Create application main window
        frame = new JFrame("J2PrinterWorks Test Application");

        printer.setParentFrame(frame);
        printer.setBusyCursor(frame, true);

        frame.getContentPane().setLayout(new BorderLayout());
        frame.setSize(350,400);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) { System.exit(0); }});

        JPanel buttonPanel = new JPanel();
        JButton printpreview = new JButton("Print Preview"); printpreview.setEnabled(false);
        JButton print = new JButton("Print"); print.setEnabled(false);
        JButton pagesetup = new JButton("Simple Page Setup"); pagesetup.setEnabled(false);
        JButton detailedpagesetup = new JButton("Detailed Page Setup"); detailedpagesetup.setEnabled(false);
        buttonPanel.setLayout(new GridLayout(2,2));
        buttonPanel.add(printpreview); buttonPanel.add(print);
        buttonPanel.add(pagesetup); buttonPanel.add(detailedpagesetup);
        frame.getContentPane().add(buttonPanel, "North");

        status = new JLabel();
        status.setHorizontalAlignment(JLabel.CENTER);
        status.setFont(new Font("SansSerif", Font.BOLD, 14));
        status.setForeground(Color.blue.darker().darker());
        status.setOpaque(true);
        status.setBackground(new Color(240,240,240));
        frame.getContentPane().add(status, "South");

        JPanel centerPanel = new JPanel();
        Color c = Color.white; centerPanel.setBackground(c);
        centerPanel.setLayout(new GridLayout(11,1));
        final JCheckBox cbtext1 = new JCheckBox("J2TextPrinter 1 (overview)",true);
        cbtext1.setBackground(c); centerPanel.add(cbtext1);
        final JCheckBox cbtable1 = new JCheckBox("J2TablePrinter 1 (100 companies)",true);
        cbtable1.setBackground(c); centerPanel.add(cbtable1);
        final JCheckBox cbtable2 = new JCheckBox("J2TablePrinter 2 (50 companies)",true);
        cbtable2.setBackground(c); centerPanel.add(cbtable2);
        final JCheckBox cbtable3 = new JCheckBox("J2TablePrinter 3 (50 companies on 1 page)",true);
        cbtable3.setBackground(c); centerPanel.add(cbtable3);
        final JCheckBox cbtree1 = new JCheckBox("J2TreePrinter 1 (file directory)",true);
        cbtree1.setBackground(c); centerPanel.add(cbtree1);
        final JCheckBox cblist1 = new JCheckBox("J2ListPrinter 1 (list of classes)",true);
        cblist1.setBackground(c); centerPanel.add(cblist1);
        final JCheckBox cbpanel1 = new JCheckBox("J2PanelPrinter 1 (many Components)",true);
        cbpanel1.setBackground(c); centerPanel.add(cbpanel1);
        final JCheckBox cbpanel2 = new JCheckBox("J2PanelPrinter 2 (Image)",true);
        cbpanel2.setBackground(c); centerPanel.add(cbpanel2);
        final JCheckBox cbpanel3 = new JCheckBox("J2PanelPrinter 3 (Frame contents)",true);
        cbpanel3.setBackground(c); centerPanel.add(cbpanel3);
        final JCheckBox cbcomponent1 = new JCheckBox("J2ComponentPrinter 1 (license agreement)",true);
        cbcomponent1.setBackground(c); centerPanel.add(cbcomponent1);
        final JCheckBox cbflow1 = new JCheckBox("J2FlowPrinter 1 (many Flowables)",true);
        cbflow1.setBackground(c); centerPanel.add(cbflow1);
        printer.addPropertyChangeListener(new MyPrintingEventHandler());
        JPanel centeringPanel = new JPanel(); centeringPanel.setBackground(c); centeringPanel.add(centerPanel);
        frame.getContentPane().add(centeringPanel, "Center");

        print.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              // test PS:
                 //J2Printer14 j2p14 = new J2Printer14();
                 //j2p14.setPageable(printer.getPageable());
                 //j2p14.printToPS("outPS.ps");
              // test PDF:
                 //printer.printToPDF("outPDF.pdf");
              // test regular printing:
                 printer.print();
            }});
        pagesetup.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { printer.showPageSetupDialog(); windowToFront(); }});
        printpreview.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              printer.showPrintPreviewDialog(frame);
              printer.setPrintPreviewPageNumber(printer.getCurrentPrintPreviewPageNumber());
              printer.setPrintPreviewScale(printer.getCurrentPrintPreviewScale());
              printer.setPrintPreviewTwoPageDisplay(printer.isCurrentPrintPreviewTwoPageDisplay());
              //test: printer.disposePrintPreviewDialogObjects();
            }});
        detailedpagesetup.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { new J2PrinterWorksPageSetupDialog(frame, printer).setVisible(true); }});

        Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
        Rectangle frameDim = frame.getBounds();
        frame.setLocation((screenDim.width-frameDim.width)/2, (screenDim.height-frameDim.height)/2);
        frame.setVisible(true);
        status.setText("Begin document creation...");


// make J2TextPrinter instance textPrinter1
        String path = new File("images").getAbsolutePath();

        JTextPane text1 = J2TextPrinter.makeHTMLPane("<html><center>"
              + "<b><font face=Serif size=+1>A J2PrinterWorks Test Document</font>"
              + "<br><font face=Serif size=+0>Wildcrest Associates</font></b>"
              + "<br><font face=Monospaced size=-1>http://www.wildcrest.com</font></tt>"
              + "<br><font face=SansSerif size=-1>January 6, 2009"
              + "<font face=SansSerif size=-3><br>&nbsp;</font>"
              + "<hr align=center width=50%>"
              + "<font face=SansSerif size=-3><br>&nbsp;</font>"

              + "<font face=Serif size=-1><i>Abstract</i></font></center>"
              + "<font face=Serif size=-2>This document is designed for testing the J2PrinterWorks "
              + "Java 2 printing component from Wildcrest Associates.  "
              + "It will illustrate the use of each of the J2PrinterWorks printing components: "
              + "J2TextPrinter, J2TablePrinter, "
              + "J2TreePrinter, J2ListPrinter, J2PanelPrinter, J2ComponentPrinter, and J2FlowPrinter.  "
              + "It also shows how "
              + "J2PrinterWorks supports properties like headers/footers (which can be different on "
              + "first vs. following pages of a given component), "
              + "margins, orientation, centering, and scaling.<br></font>"

              + "<br><font face=Serif size=+0><b>Pageables</b></font>"
              + "<br><font face=Serif size=-1>J2PrinterWorks can create "
              + "and print an overall document consisting of one or "
              + "more multi-page java.awt.print.Pageable instances, "
              + "where each Pageable starts and ends on printer page boundaries.  "
              + "<br><br>J2PrinterWorks comes with 7 Pageable components:"
              + "<ul compact type=circle>"
              + "<li><b>J2TextPrinter</b> - paginates any JTextPane (or JTextPane subclass)</li>"
              + "<li><b>J2TablePrinter</b> - paginates any JTable (or JTable subclass)</li>"
              + "<li><b>J2TreePrinter</b> - paginates any JTree (or JTree subclass)</li>"
              + "<li><b>J2ListPrinter</b> - paginates any JList (or JList subclass)</li>"
              + "<li><b>J2PanelPrinter</b> - paginates any JPanel as well as simple Components and Images</li>"
              + "<li><b>J2ComponentPrinter</b> - paginates any Component (or Component subclass)</li>"
              + "<li><b>J2FlowPrinter</b> - paginates a series of "
              + "Flowable instances printed back-to-back (see below)</li></ul>"
              + "</font><font face=Serif size=-1>"
              + "In addition, you can create and intermix your own custom "
              + "java.awt.print.Pageable instances with any of the above."
              + "<br><br>This document will also demonstrate how you can "
              + "change the headers, footers, margins, centering, orientation, "
              + "and scale separately for each Pageable.  There are left, right, and center "
              + "headers and footers.  Each can be either a simple one-line String "
              + "or a JLabel defined using HTML with multiple lines, rich text, and images."
              + "They can include page numbers and date/time formatting and "
              + "can be different on the first page of any Pageable.</font>"

              + "<br><br><font face=Serif size=+0><b>Flowables</b></font>"
              + "<br><font face=Serif size=-1>In addition to supporting "
              + "Pageables, J2PrinterWorks introduces a new printing "
              + "interface called Flowable.  Like Pageables, Flowables are printable components that may span "
              + "multiple pages, but unlike Pageables, they can also begin and end in the middle of any page, "
              + "so that each Flowable starts immediately after the end of the previous Flowable without "
              + "starting at the top of the next page.  "
              + "<br><br>A J2FlowPrinter instance can group any number of Flowable instances, "
              + "one immediately following the other back-to-back down the page.  "
              + "The resulting J2FlowPrinter instance can be used as a Pageable (i.e., starting and ending on page boundaries), "
              + "but it can also be used as a Flowable, allowing you to use J2FlowPrinter instances as Flowable containers, "
              + "that is, J2FlowPrinter instances can be nested inside other J2FlowPrinter instances.  "
              + "<p>The following Flowables are provided by J2PrinterWorks:"
              + "<ul compact type=circle>"
              + "<li><b>J2TextPrinter</b> - a Flowable (as well as a Pageable) for JTextPane</li>"
              + "<li><b>J2TablePrinter</b> - a Flowable (as well as a Pageable) for JTable</li>"
              + "<li><b>J2TreePrinter</b> - a Flowable (as well as a Pageable) for JTree</li>"
              + "<li><b>J2ListPrinter</b> - a Flowable (as well as a Pageable) for JList</li>"
              + "<li><b>J2PanelPrinter</b> - a Flowable (as well as a Pageable) for JPanel</li>"
              + "<li><b>J2ComponentPrinter</b> - a Flowable (as well as a Pageable) for Component</li>"
              + "<li><b>J2FlowPrinter</b> - a Flowable as well as a Pageable for any sequence of Flowables</li>"
              + "<li><b>HorizontalLine</b> - a Flowable for drawing a horizontal line between successive Flowables</li>"
              + "<li><b>VerticalGap</b> - a Flowable for leaving a gap between successive Flowables</li>"
              + "<li><b>PageEject</b> - a Flowable for starting the next Flowable at the top of a new page</li></ul>"
              + "</font><font face=Serif size=-1>You can also create and intermix your own custom "
              + "com.wildcrest.j2printerworks.Flowable instances."
              + "<br><br>By stringing together a series of Flowables, "
              + "you can do things like include a JTable, a JTree, a JList, a Component, an Image, or a Graphics drawing "
              + "within an overall text document made up of multiple JTextPane instances, "
              + "placing titles above and/or captions below any of these components.  "
              + "Each Flowable can be horizontally justified left, right, or center within a page, "
              + "and vertically justified top, bottom, or center within the remaining space in the page.  "
              + "Each flowable can be scaled independently, so figures and tables can be inserted "
              + "into a text document at different relative sizes.</font>"

              + "<br><br><font face=Serif size=+0><b>J2Printer, J2Printer14, and J2PrinterWebStart</b></font>"
              + "<br><font face=Serif size=-1>J2PrinterWorks manages the Java printing process "
              + "using a class called J2Printer.  "
              + "A simple printing job might consist of the following code:</font>"
              + "<center><table compact width=520 cellpadding=0 cellspacing=0 height=0>"
              + "<tr><font face=monospaced size=-1>J2Printer printer = new J2Printer();</font></tr>"
              + "<tr><font face=monospaced size=-1>printer.setLeftFooter(\"Overall footer\"); // global properties</font></tr>"
              + "<tr><font face=monospaced size=-1>J2TextPrinter j2text1 = new J2TextPrinter(yourJTextPane);</font></tr>"
              + "<tr><font face=monospaced size=-1>J2TablePrinter j2table1 = new J2TablePrinter(yourJTable);</font></tr>"
              + "<tr><font face=monospaced size=-1>j2text1.setLeftFooter(\"My footer\"); // this Pageable properties</font></tr>"
              + "<tr><font face=monospaced size=-1>printer.addPageable(j2text1);</font></tr>"
              + "<tr><font face=monospaced size=-1>printer.addPageable(j2table1);</font></tr>"
              + "<tr><font face=monospaced size=-1>printer.print();</tr></font></table></center>"
              + "<font face=Serif size=-1>The J2Printer class supports printing in a background thread, "
              + "detecting print progress events, and display of page setup, print preview, and print dialogs.  "
              + "The properties you set at the J2Printer level will be used for all Pageables in your "
              + "document, unless any given Pageable sets its own values for these properties.  "
              + "<br><br>The JDK 1.4 release extends Java printing capabilities to include "
              + "print request attributes, printer discovery, programmatic printer selection, and cross-platform page setup "
              + "and print dialogs.  J2PrinterWorks provides a special subclass of J2Printer called J2Printer14 "
              + "which supports these additional capabilities (but requires JDK 1.4 or later, whereas "
              + "all other J2PrinterWorks classes can build and run under any JDK 1.2 or later).  "
              + "To use the JDK 1.4 printing features, simply substitute:</font>"
              + "<center><table compact width=520 cellpadding=0 cellspacing=0 height=0>"
              + "<tr><font face=monospaced size=-1>J2Printer14 printer = new J2Printer14();</font></tr>"
              + "</table></center>"
              + "<font face=Serif size=-1>in the code sample above.  This will allow you to begin utilizing the JDK 1.4 printing capabilities.  All the J2PrinterWorks "
              + "classes other than J2Printer14 can build and run under any JDK 1.2 or later.</font>"

              + "<br><br><font face=Serif size=-1>Java Web Start applications use a somewhat different printing model employing "
              + "the JNLP API which allows downloadable Java Web Start applications to print.  "
              + "To support Java Web Start, J2PrinterWorks provides a special subclass of J2Printer "
              + "called J2PrinterWebStart "
              + "which supports these additional capabilities but requires the presence of jnlp.jar provided "
              + "as part of Java Web Start.  "
              + "To perform Java Web Start printing, simply substitute:</font>"
              + "<center><table compact width=520 cellpadding=0 cellspacing=0 height=0>"
              + "<tr><font face=monospaced size=-1>J2PrinterWebStart printer = new J2PrinterWebStart();</font></tr>"
              + "</table></center>"
              + "<font face=Serif size=-1>in the code sample above.  This will allow you to print from a downloadable "
              + "Java Web Start application.</font>"

              + "<br><br><font face=Serif size=+0><b>J2TextPrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2TextPrinter instance will print a JTextPane as a Pageable or a Flowable.  "
              + "Everything since the beginning of \"A J2PrinterWorks Test Document\" has been "
              + "a single J2TextPrinter instance used as a Pageable.  This J2TextPrinter instance contains "
              + "a JTextPane instance specified using one long HTML string.  "
              + "The JTextPane could also be "
              + "specified using the Java class StyledDocument or by reading an RTF or HTML file.  "
              + "HTML can contain images like this: "
              // alternative: + "<img src=\""+ClassLoader.getSystemResource("images/check.gif")+"\">&nbsp;or this: "
              // alternative: + "<img src=\""+ClassLoader.getSystemResource("images/ex.gif")+"\">&nbsp;or even this:"
              // alternative: + "<center><img src=\""+ClassLoader.getSystemResource("images/J2PrinterWorksC32.gif")+"\"></center>"
              + "<img src=\"file:///"+path+"/check.gif\">&nbsp;or this: "
              + "<img src=\"file:///"+path+"/ex.gif\">&nbsp;or even this:"
              + "<center><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></center>"
              + "Elsewhere in this document we have demonstrated the use of HTML tables, HTML bulleted lists, and "
              + "HTML separators like line breaks and horizontal lines.  "
              + "<br><br>J2TextPrinter will reflow (rewrap) the contents of the JTextPane to fit the width of the page, "
              + "taking the current page size "
              + "and margins into account.  Alternatively, you can turn on \"WYSIWYG\" mode for your J2TextPrinter "
              + "instance to cause the printed pages to retain the layout of the specified JTextPane.  "
              + "J2TextPrinter paginates a JTextPane instance over multiple pages by breaking the "
              + "text on line boundaries.</font>"

              + "<br><br><font face=Serif size=+0><b>J2TablePrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2TablePrinter instance will print a JTable as a Pageable or a Flowable.  "
              + "J2TablePrinter can paginate JTable instances across multiple pages both vertically "
              + "and horizontally, breaking the JTable on row "
              + "and column boundaries.  Rows and columns can be of variable height and width.  "
              + "Column headings can appear on all pages, only on the top pages, or not at all.  "
              + "The JTable can be left, right, or center justified horizontally, "
              + "and top, bottom, or center justified vertically.  "
              + "You can also control whether the gridlines and outside lines are to be printed.  "
              + "J2TablePrinter prints the JTable \"WYSIWYG\", so that you can specify any foreground "
              + "and background colors, fonts, intercell spacing, or even define your own "
              + "custom cell renderers, and all of these will be reflected in the printed output.</font>"

              + "<br><br><font face=Serif size=+0><b>J2TreePrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2TreePrinter instance will print a JTree as a Pageable or a Flowable.  "
              + "J2TreePrinter paginates a JTree across multiple pages vertically, "
              + "breaking the JTree on row (tree node) boundaries.  A JTree that is too wide horizontally "
              + "will be rescaled to fit the page width, with the resulting JTree paginated "
              + "vertically across multiple pages as required.  "
              + "The JTree can be left, right, or center justified horizontally, and "
              + "top, bottom, or center justified vertically.  "
              + "You can draw an outside line of any color around the JTree.</font>"

              + "<br><br><font face=Serif size=+0><b>J2ListPrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2ListPrinter instance will print a JList as a Pageable or a Flowable.  "
              + "J2ListPrinter paginates a JList across multiple pages vertically, "
              + "breaking the JList on list item boundaries.  A JList that is too wide horizontally "
              + "will be rescaled to fit the page width, with the resulting JList paginated "
              + "vertically across multiple pages as required.  "
              + "The JList can be left, right, or center justified horizontally, and can be "
              + "top, bottom, or center justified vertically.  "
              + "You can draw an outside line of any color around the JList and "
              + "use the HORIZONTAL_WRAP and VERTICAL_WRAP layout orientations supported by JDK 1.4.</font>"

              + "<br><br><font face=Serif size=+0><b>J2PanelPrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2PanelPrinter instance will print "
              + "any JPanel and simple Components or Images as a Pageable or a Flowable.  "
              + "The JPanel may contain one or more Components using a "
              + "a user-specified LayoutManager, or it can print an individual Component or Image.  "
              + "The JPanel, Component, or Image should either fit within a printed page, or be appropriate for "
              + "one of the following pagination rules: "
              + "<ul compact type=circle>"
              + "<li><b>shrink-to-fit</b> - scales the output to fit within a single "
              + "page</li>"
              + "<li><b>shrink-to-width</b> - scales output to fit a single "
              + "page wide, paginating multiple pages vertically</li>"
              + "<li><b>tile</b> - breaks pages horizontally & vertically using pixel boundary "
              + "pagination, like an Image</li>"
              + "<li><b>break-on-components</b> - breaks pages horizontally & vertically "
              + "on Component boundaries</li></ul>"
              + "The JPanel, Component, or Image can be left, right, or center justified horizontally or "
              + "top, bottom, or center justified vertically.  "
              + "You can set the background color and/or "
              + "draw a surrounding outside line.</font>"

              + "<br><br><font face=Serif size=+0><b>J2ComponentPrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2ComponentPrinter instance will print "
              + "any Component, Container of Components, or Image "
              + " as a Pageable or a Flowable.  "
              + "Because of its general pagination capabilities, J2ComponentPrinter can paginate "
              + "large, multi-page Components, including many third-party components, and provides "
              + "an alternative means for printing Swing components such as JTextPane, JTable, JTree, etc. "
              + "The J2ComponentPrinter pagination rules can be separately specified in "
              + "horizontal and vertical directions.  They are: "
              + "<ul compact type=circle>"
              + "<li><b>shrink-to-fit</b> - scales the output to fit within a single "
              + "page width and/or height</li>"
               + "<li><b>tile</b> - breaks pages horizontally and/or vertically using pixel boundary "
              + "pagination, like an Image</li>"
              + "<li><b>break-on-components</b> - breaks pages horizontally and/or vertically "
              + "on Component boundaries</li>"
              + "<li><b>break-on-colors</b> - breaks pages horizontally and/or vertically "
              + "on specified color boundaries.</li></ul>"
              + "The break-on-colors mode in particular gives J2ComponentPrinter the ability to "
              + "print a wide variety of different Components, using a break color such as Color.white to break on "
              + "\"white space\".  The Component can be left, right, or center justified horizontally or "
              + "top, bottom, or center justified vertically.  "
              + "You can set the background color and/or "
              + "draw a surrounding outside line.</font>"

              + "<br><br><font face=Serif size=+0><b>J2FlowPrinter</b></font>"
              + "<br><font face=Serif size=-1>A J2FlowPrinter instance will print a series of Flowables as a Pageable or a Flowable.  "
              + "Multiple J2TextPrinter, J2TablePrinter, J2TreePrinter, J2ListPrinter, J2ComponentPrinter "
              + "J2PanelPrinter, and even other J2FlowPrinter instances can be intermixed along with "
              + "HorizontalLine, VerticalGap, and PageEject instances, as well as your own custom Flowables, and printed "
              + "back-to-back down the printed page.  Each component can be separately justified left, right, or center horizontally "
              + "or top, bottom, or center vertically.  "
              + "J2FlowPrinter provides a effective way to embed tables, images, "
              + "and/or drawings each with their own titles and/or captions in an overall text-based document.</font>"

              + "<br><br><font face=Serif size=+0><b>Other classes</b></font>"
              + "<br><font face=Serif size=-1>J2PrinterWorks contains a number of \"helper\" classes "
              + "which can facilitate printing.  These include "
              + "<b>J2Label</b>, which is a JLabel subclass "
              + "for making HTML JLabels that work "
              + "consistently across JDK 1.2, 1.3, and 1.4, "
              + "<b>ImagePanel</b>, a JPanel subclass for displaying a scaled, centered Image with a possible border, "
              + "<b>PrinterEventHandler</b>, a convenience class that facilitates responding to "
              + "J2PrinterWorks print progress events, "
              + "and <b>J2Pageable</b>, an abstract base class that provides a full J2PrinterWorks Pageable "
              + "(supporting headers, footers, margins, centering, orientation, and scaling) "
              + "given an implementation of the methods of Flowable."
              + "</font>"

              + "<br><br><font face=Serif size=+0><b>Additional Information</b></font>"
              + "<br><font face=Serif size=-1>J2PrinterWorks is available in Individual or Site Licenses for either Binary or Source Code versions.  "
              + "Pricing is on a per-developer basis and there are no run-time royalties for distributing J2PrinterWorks as part of "
              + "your software.  To learn more about J2PrinterWorks, view print samples or the complete documentation, "
              + "download the Free Trial Version, or purchase on-line, "
              + "please visit the Wildcrest Associates web site at</font> "
              + "<font face=monospaced size=-1 color=#0000ff><u>http://www.wildcrest.com</u></font>."
              + "<br>&nbsp;"

              + "</html>"
        );

        text1.setEditable(false);      // so get no editing caret printed if you accidently click in JTextPane
        status.setText("make textPrinter 1 (overview)");
        final J2TextPrinter textPrinter1 = new J2TextPrinter(text1);
        textPrinter1.setCloningUsed(false);  // due to serialization bug in Java, borders are lost in HTML tables if cloningUsed is true
        if (System.getProperty("java.version").startsWith("1.2")) textPrinter1.setDirectPrint(true);
        // can insert big image and test: textPrinter1.setMaximumPaginationGap(.5);


        textPrinter1.setLeftFooter(J2Printer.FIRST,new J2Label("<html><font size=-1>J2PrinterWorks Test Application<br>J2PrinterWorks "
            + printer.getVersion() + " / JDK " + System.getProperty("java.version") + "</html>"));
        textPrinter1.setCenterFooter(J2Printer.FIRST, new J2Label("<html><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></html>"));
        textPrinter1.setRightFooter(J2Printer.FIRST,new J2Label("<html><div align=right><font size=-1>|||EEE, MMM d, yyyy|||<br>|||hh:mm:ss a zzz|||</html>"));
        textPrinter1.setFooterStyle(J2Printer.FIRST, J2Printer.NONE);

        if (onScreen) {
            JFrame textFrame = new JFrame("J2TextPrinter overview test");
            textFrame.getContentPane().add(new JScrollPane(text1));
            textFrame.setSize(600,600);
            textFrame.setLocation(10,10);
            textFrame.setVisible(true);
        }


// make J2TablePrinter instance tablePrinter1
		// The Vector.addElement calls below may be flagged as warnings by the javac compiler.
		// If you are using JDK 1.5 or later, generic declarations of Vector will prevent the
		// warnings:
		//Vector<String> colsTable1 = new Vector<String>(); // for JDK 1.5 or later
        Vector colsTable1 = new Vector();
        colsTable1.addElement("Rank"); colsTable1.addElement("Symbol");
        colsTable1.addElement("Company"); colsTable1.addElement("Price");
        colsTable1.addElement("Shares"); colsTable1.addElement("Market Cap");
        //Vector<Vector> dataTable1 = new Vector<Vector>(); // for JDK 1.5 or later
        Vector dataTable1 = new Vector();
        try {
            BufferedReader in = new BufferedReader(new FileReader("F100.txt"));
    	    in.readLine();        // throw away first line (headers)
            while (in.ready()) {
    	        String line = in.readLine();
    	        StringTokenizer st = new StringTokenizer(line,"\t");
                //Vector<String> vec = new Vector<String>(); // for JDK 1.5 or later
                Vector vec = new Vector();
                for (int i=0; i<6; i++) vec.addElement(st.nextToken());
                dataTable1.addElement(vec);
            }
            in.close();
        } catch (Exception e) { }

        JTable table1 = new JTable(dataTable1, colsTable1);
        table1.getColumnModel().getColumn(0).setPreferredWidth(45);
        table1.getColumnModel().getColumn(1).setPreferredWidth(55);
        table1.getColumnModel().getColumn(2).setPreferredWidth(205);
        table1.getColumnModel().getColumn(3).setPreferredWidth(45);
        table1.getColumnModel().getColumn(4).setPreferredWidth(55);
        table1.getColumnModel().getColumn(5).setPreferredWidth(80);
        table1.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

        status.setText("make tablePrinter 1 (100 companies)");

        final J2TablePrinter tablePrinter1 = new J2TablePrinter(table1);

        /* test zero-size JTable:
        Object [] columnsA0 = { "a", "b", "c", "d", "e", "f"};
        Object [][] dataA0 = { };
        JTable tableAAA = new JTable(dataA0, columnsA0);
        final J2TablePrinter tablePrinter1 = new J2TablePrinter(tableAAA);
        tablePrinter1.setColumnHeaderPrinting(J2TablePrinter.NONE);
        */

        tablePrinter1.setLeftFooter(J2Printer.FIRST,new J2Label("<html><font face=SansSerif size=-1>This is a <b>J2TablePrinter</b><br>printing a JTable<br>as a Pageable</font></html>"));
        tablePrinter1.setLeftFooter(J2Printer.REST,new J2Label("<html><font face=SansSerif size=-1>More of a J2TablePrinter<br>printing a JTable<br>as a Pageable</font></html>"));
        tablePrinter1.setRightFooter(J2Printer.FIRST,new J2Label("<html><font face=Serif size=-1>Note the center footer<br>on the rest of pages is the<br>global (J2Printer) value</html>"));
        tablePrinter1.setCenterFooter(J2Printer.FIRST,new J2Label("<html><font face=Serif size=-1>The left, center, and right footers are<br>JLabel instances and can have images,<br>multiple lines, & <i>different fonts</i></font></html>", new ImageIcon("images"+File.separator+"check.gif"),JLabel.RIGHT));
        tablePrinter1.setRightFooter(J2Printer.REST,new J2Label("<html><font face=Serif size=-1>Each Pageable has separate<br>headers & footers for the<br><i>first</i> page and <i>rest</i> of the pages</font></html>"));

        if (onScreen) {
            JFrame tableFrame = new JFrame("J2TablePrinter 100 companies test");
            tableFrame.getContentPane().add(new JScrollPane(table1));
            tableFrame.pack();
            tableFrame.setLocation(30,30);
            tableFrame.setVisible(true);
        }

// make J2TablePrinter instance tablePrinter2
		// The Vector.addElement calls below may be flagged as warnings by the javac compiler.
		// If you are using JDK 1.5 or later, a generic declaration of Vector will prevent the
		// warnings:
		//Vector<String> colsTable2 = new Vector<String>(); // for JDK 1.5 or later
        Vector colsTable2 = new Vector();
        colsTable2.addElement("Rank"); colsTable2.addElement("Symbol");
        colsTable2.addElement("Company"); colsTable2.addElement("Price");
        colsTable2.addElement("Shares"); colsTable2.addElement("Market Cap");
        colsTable2.addElement("Revenue"); colsTable2.addElement("F500 rank");
        colsTable2.addElement("MarketCap/Rev"); colsTable2.addElement("MarketCap/Rev rank");
        colsTable2.addElement("Employees"); colsTable2.addElement("Rev/Emp");
        colsTable2.addElement("Rev/Emp rank");

        //Vector<Vector> dataTable2 = new Vector<Vector>(); // for JDK 1.5 or later
        Vector dataTable2 = new Vector();
        try {
            BufferedReader in = new BufferedReader(new FileReader("F100.txt"));
    	    in.readLine();        // throw away first line (headers)
            for (int k=0; k<50; k++) {
    	        String line = in.readLine();
    	        StringTokenizer st = new StringTokenizer(line,"\t");
                //Vector<String> vec = new Vector<String>(); // for JDK 1.5 or later
                Vector vec = new Vector();
                for (int i=0; i<13; i++) vec.addElement(st.nextToken());
                dataTable2.addElement(vec);
            }
            in.close();
        } catch (Exception e) { }

        JTable table2 = new JTable(dataTable2, colsTable2);
        table2.getColumnModel().getColumn(0).setPreferredWidth(45);
        table2.getColumnModel().getColumn(1).setPreferredWidth(55);
        table2.getColumnModel().getColumn(2).setPreferredWidth(205);
        table2.getColumnModel().getColumn(3).setPreferredWidth(45);
        table2.getColumnModel().getColumn(4).setPreferredWidth(55);
        table2.getColumnModel().getColumn(5).setPreferredWidth(80);
        table2.getColumnModel().getColumn(6).setPreferredWidth(70);
        table2.getColumnModel().getColumn(7).setPreferredWidth(75);
        table2.getColumnModel().getColumn(8).setPreferredWidth(105);
        table2.getColumnModel().getColumn(9).setPreferredWidth(130);
        table2.getColumnModel().getColumn(10).setPreferredWidth(80);
        table2.getColumnModel().getColumn(11).setPreferredWidth(70);
        table2.getColumnModel().getColumn(12).setPreferredWidth(90);
        table2.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

        status.setText("make tablePrinter 2 (50 companies)");
        final J2TablePrinter tablePrinter2 = new J2TablePrinter(table2);
        tablePrinter2.setOrientation(J2Printer.LANDSCAPE);
        tablePrinter2.setColumnHeaderPrinting(J2TablePrinter.TOP_PAGES);

        tablePrinter2.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This <b>J2TablePrinter</b> instance is<br>printing another JTable<br>as a Pageable</font></html>"));
        // alternative: tablePrinter2.setCenterFooter(new J2Label("<html><img src=\""+ClassLoader.getSystemResource("images/J2PrinterWorksC32.gif")+"\"></html>"));
        tablePrinter2.setCenterFooter(new J2Label("<html><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></html>"));
        tablePrinter2.setRightFooter(J2Printer.FIRST,new J2Label("<html><font face=Serif size=-1>Each Pageable has its own orientation.<br>This Pageable is <b>LANDSCAPE</b> and<br>paginated horizontally & vertically</html>"));
        tablePrinter2.setRightFooter(J2Printer.REST,new J2Label("<html><font face=Serif size=-1>Note this JTable only prints<br>column headings on the top pages<br>and not on any subsequent pages.</html>"));

        if (onScreen) {
            JFrame tableFrame2 = new JFrame("J2TablePrinter 50 companies test");
            tableFrame2.getContentPane().add(new JScrollPane(table2));
            tableFrame2.pack();
            tableFrame2.setLocation(50,50);
            tableFrame2.setVisible(true);
        }


 // make J2TablePrinter instance tablePrinter3
        status.setText("make tablePrinter 3 (50 companies 1 page)");
        final J2TablePrinter tablePrinter3 = new J2TablePrinter(table2);
        tablePrinter3.setOrientation(J2Printer.LANDSCAPE);
        tablePrinter3.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This <b>J2TablePrinter</b> instance<br>is printing this JTable (again)<br>as a Pageable</font></html>"));
        tablePrinter3.setRightFooter(new J2Label("<html><font face=Serif size=-1>Each Pageable has its own scaling<br>factor.  This Pageable requested the<br>entire JTable to fit on one page.</font></html>"));
        //tablePrinter3.setMaximumPages(1, 1);  // must be called AFTER any content, headers, footers, margins, etc. are changed

        // tests:
        //tablePrinter3.setPrintArea(3,10,8,35);

        tablePrinter3.setColumnHeaderPrinting(J2TablePrinter.ALL_PAGES);
        //tablePrinter3.setColumnHeaderPrinting(J2TablePrinter.TOP_PAGES);
        //tablePrinter3.setColumnHeaderPrinting(J2TablePrinter.NO_PAGES);

        tablePrinter3.setRowHeaderPrinting(J2TablePrinter.ALL_PAGES);
        //tablePrinter3.setRowHeaderPrinting(J2TablePrinter.LEFT_PAGES);
        //tablePrinter3.setRowHeaderPrinting(J2TablePrinter.NO_PAGES);

        tablePrinter3.setHorizontalPageRule(J2TablePrinter.SHRINK_TO_FIT);
        //tablePrinter3.setHorizontalPageRule(J2TablePrinter.BREAK_ON_COLUMNS);
        //tablePrinter3.setHorizontalPageRule(J2TablePrinter.BREAK_ON_COLOR);
        //tablePrinter3.setHorizontalBreakColor(table2.getGridColor());

        tablePrinter3.setVerticalPageRule(J2TablePrinter.SHRINK_TO_FIT);
        //tablePrinter3.setVerticalPageRule(J2TablePrinter.BREAK_ON_ROWS);
        //tablePrinter3.setVerticalPageRule(J2TablePrinter.BREAK_ON_COLOR);
        //tablePrinter3.setVerticalBreakColor(table2.getGridColor());
        //tablePrinter3.setOverlap(true);
        //tablePrinter3.setOutsideLines(true);


// make J2TreePrinter instance treePrinter1
        JTree tree1 = makeFileTree(new File("..").getAbsolutePath());
        expandAllRows(tree1, 100);
        status.setText("make treePrinter 1 (file directory)");
        final J2TreePrinter treePrinter1 = new J2TreePrinter(tree1);
        treePrinter1.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This is a <b>J2TreePrinter</b><br>printing a JTree<br>as a Pageable</font></html>"));
        treePrinter1.setCenterFooter(J2Printer.REST,new J2Label("<html><font face=Serif size=-1>Note the center footer<br>on the first page was the<br>global (J2Printer) value</html>"));
        treePrinter1.setRightFooter(new J2Label("<html><font face=Serif size=-1>J2TreePrinter shrinks its JTree<br>to fit to <u>one page</u> wide, broken<br>over multiple pages vertically</font></html>"));

        if (onScreen) {
            JFrame treeFrame = new JFrame("J2TreePrinter file directory test");
            treeFrame.getContentPane().add(new JScrollPane(tree1));
            treeFrame.pack();
            treeFrame.setLocation(70,70);
            treeFrame.setVisible(true);
        }


// make J2ListPrinter instance listPrinter1 (list of classes)
        status.setText("make listPrinter 1 (list of classes)");
        String[] listData = { "J2Printer", "J2Printer14", "J2PrinterWebStart","Flowable",
            "J2Pageable", "J2TextPrinter", "J2TablePrinter", "J2TreePrinter", "J2ListPrinter",
            "J2ComponentPrinter", "J2PanelPrinter", "J2FlowPrinter", "HorizontalLine", "VerticalGap",
            "PageEject", "J2Label", "ImagePanel", "PrintingEventHandler" };
        JList list1 = new JList(listData);

        final J2ListPrinter listPrinter1 = new J2ListPrinter();
        listPrinter1.setFooterStyle(J2Printer.NONE);
        listPrinter1.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This <b>J2ListPrinter</b><br>is printing the<br>contents of a JList</font></html>"));
        listPrinter1.setCenterFooter(new J2Label("<html><font face=Serif size=-1><div align=center>Under JDK 1.4 or later JList can also use<br>HORIZONTAL_WRAP and VERTICAL_WRAP</div></font></html>"));
        listPrinter1.setRightFooter(J2Printer.FIRST, new J2Label("<html><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></html>"));
        listPrinter1.setList(list1);
        if (onScreen) {
          JFrame panelFrame3 = new JFrame("J2ListPrinter test");
          panelFrame3.getContentPane().add(list1);
          panelFrame3.pack();
          panelFrame3.setLocation(90, 90);
          panelFrame3.setVisible(true);
        }


// make J2PanelPrinter instance panelPrinter1
        status.setText("make panelPrinter 1 (many Components)");
        final J2PanelPrinter panelPrinter1 = new J2PanelPrinter();
        JPanel componentPanel = new JPanel();
        componentPanel.setBackground(Color.white);
        componentPanel.setPreferredSize(new Dimension(425,400));    // required

        panelPrinter1.setHorizontalAlignment(J2Printer.CENTER);
        panelPrinter1.setVerticalAlignment(J2Printer.CENTER);
        panelPrinter1.setPageRule(J2PanelPrinter.BREAK_ON_COMPONENTS);
        // or test other rules: panelPrinter1.setPageRule(J2PanelPrinter.SHRINK_TO_FIT);
        // panelPrinter1.setPageRule(J2PanelPrinter.TILE);
        panelPrinter1.setPanel(componentPanel);

        Object[] columns1 = {"This", "is", "a", "JTable"};
        Object[][] data1 = {{"table", "", "", ""}, {"", "data", "", ""}, {"", "", "goes", ""}, {"", "", "", "here"}};
        JScrollPane jsp = new JScrollPane(new JTable(data1,columns1)); jsp.setPreferredSize(new Dimension(400,83));
        componentPanel.add(jsp);                                 // add a JTable

        componentPanel.add(new JButton("This is a JButton"));    // add a JButton

        componentPanel.add(new JPanel() {                        // add some graphics in a paint of JPanel
            public Dimension getPreferredSize() { return new Dimension(180,150); }
            public void paint(Graphics g) {
                g.drawLine(25,135,25,25); g.drawLine(15,125,175,125);
                g.setColor(Color.yellow); g.fillRect(50,100,25,25);
                g.setColor(Color.black); g.drawRect(50,100,25,25);
                g.setColor(Color.pink); g.fillRect(85,75,25,50);
                g.setColor(Color.black); g.drawRect(85,75,25,50);
                g.setColor(Color.orange); g.fillRect(120,50,25,75);
                g.setColor(Color.black); g.drawRect(120,50,25,75);
                g.setFont(new Font("SansSerif",Font.PLAIN,10));
                g.drawString("Graphics",40,95);
                g.drawString("in a",90,70);
                g.drawString("JPanel",115,45);
            }
        } );

        componentPanel.add(new JTextArea("This is a JTextArea\nwhich contains several lines\nof text using a single font")); // add a JTextArea

        componentPanel.add(new JLabel("<html>This is a JLabel with<br>multiple lines and <i>an image</i></html>",
            new ImageIcon("images"+File.separator+"check.gif"),JLabel.RIGHT));      // add a JLabel

        componentPanel.add(new JPanel() {                        // add a .gif
            public Dimension getPreferredSize() { return new Dimension(200,40); }
            public void paint(Graphics g) {
                g.drawString("An image in a JPanel:",0,25);
                g.drawImage(new ImageIcon("images"+File.separator+"J2PrinterWorksC32.gif").getImage(),125,0,this); } } );

        JCheckBox jcb = new JCheckBox("This is a JCheckBox", true);
        jcb.setBackground(Color.white);
        componentPanel.add(jcb);

        componentPanel.add(new JEditorPane("text/html","This is a <b>JEditorPane</b> using <i>HTML</i> to get <tt>different fonts.</tt>"));

        panelPrinter1.setFooterStyle(J2Printer.LINE);
        panelPrinter1.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This is a <b>J2PanelPrinter</b><br>printing a JPanel<br>as a Pageable</font></html>"));
        panelPrinter1.setCenterFooter(new J2Label("<html><font face=Serif size=-1>Note header & footer styles<br>can be BOX, LINE (as in<br>this case), or NONE</font></html>"));
        panelPrinter1.setRightFooter(new J2Label("<html><font face=Serif size=-1>J2PanelPrinter can<br><b>shrink-to-fit</b> to one page<br>or <b>tile</b> across multiple pages</font></html>"));

        if (onScreen) {
            JFrame componentFrame = new JFrame("J2PanelPrinter many Components test");
            componentFrame.getContentPane().add(new JScrollPane(componentPanel));
            componentFrame.pack();
            componentFrame.setLocation(110,110);
            componentFrame.setVisible(true);
        }


    // make J2PanelPrinter instance panelPrinter2 (using Image constructor)
            status.setText("make panelPrinter 2 (Image)");
            Image img = new ImageIcon("images"+File.separator+"Yellowstone.jpg").getImage();
            final J2PanelPrinter panelPrinter2 = new J2PanelPrinter(img);
            panelPrinter2.setOrientation(J2Printer.LANDSCAPE);
            panelPrinter2.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This <b>J2PanelPrinter</b><br>instance is printing<br>an Image</font></html>"));
            // alternative: panelPrinter2.setCenterFooter(new J2Label("<html><img src=\""+ClassLoader.getSystemResource("images/J2PrinterWorksC32.gif")+"\"></html>"));
            panelPrinter2.setCenterFooter(new J2Label("<html><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></html>"));
            panelPrinter2.setRightFooter(new J2Label("<html><font face=Serif size=-1>J2PanelPrinter has a<br>constructor for Images, and<br>can either <i>shrink-to-fit</i> or <i>tile</i></font></html>"));

            if (onScreen) {
                JFrame panelFrame2 = new JFrame("J2PanelPrinter Image test");
                ImagePanel imagePanel = new ImagePanel(img);
                panelFrame2.getContentPane().add(imagePanel);
                panelFrame2.pack();
                panelFrame2.setLocation(130,130);
                panelFrame2.setVisible(true);
            }


// make J2PanelPrinter instance panelPrinter3 (contents of a JFrame)
            status.setText("make panelPrinter 3 (Frame contents)");
            final J2PanelPrinter panelPrinter3 = new J2PanelPrinter();
            panelPrinter3.setHorizontalAlignment(J2Printer.CENTER);
            panelPrinter3.setVerticalAlignment(J2Printer.CENTER);
            panelPrinter3.setOutsideLines(true);
            panelPrinter3.setFooterStyle(J2Printer.NONE);
            panelPrinter3.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This <b>J2PanelPrinter</b><br>is printing the<br>contents of a JFrame</font></html>"));
            panelPrinter3.setCenterFooter(new J2Label("<html><font face=Serif size=-1>HTML footers can also have<br>dates: <b>|||EEE, MMM d, yyyy|||</b><br>and page numbers: <u>Page ###</u></font></html>"));
            panelPrinter3.setRightFooter(new J2Label("<html><font face=Serif size=-1>Note header & footer styles<br>can be BOX, LINE, or<br>NONE (as in this case)</font></html>"));
            panelPrinter3.setComponent(frame.getContentPane()); // print contents of our main window
            //doesn't work, Java doesn't support: panelPrinter3.setComponent(frame);

// make J2ComponentPrinter instance componentPrinter1 (license agreement)
            status.setText("make componentPrinter 1 (license agreement)");
            StringBuffer buffer = new StringBuffer();
            try {
              BufferedReader inData = new BufferedReader(new FileReader(
                  "J2PrinterWorksFreeTrialLicense.html"));
              String inLine;
              while ( (inLine = inData.readLine()) != null) {
                buffer.append(inLine);
                buffer.append('\n');
              }
              inData.close();
            }
            catch (IOException e) { }
            String str = buffer.toString();
            JTextPane comp1 = new JTextPane();
            comp1.setContentType("text/html");
            comp1.setText(str);

            J2ComponentPrinter.setSizeForWidth((int)printer.getBodyWidth(),comp1);

            if (onScreen) {
                JFrame compFrame1 = new JFrame("J2ComponentPrinter test");
                compFrame1.getContentPane().add(new JScrollPane(comp1));
                compFrame1.pack();
                compFrame1.setLocation(150,150);
                compFrame1.setVisible(true);
            }

            final J2ComponentPrinter componentPrinter1 = new J2ComponentPrinter();
            componentPrinter1.setFooterStyle(J2Printer.LINE);
            componentPrinter1.setLeftFooter(new J2Label("<html><font face=SansSerif size=-1>This <b>J2ComponentPrinter</b><br>is printing a JTextArea<br>containg a license agreement</font></html>"));
            componentPrinter1.setCenterFooter(new J2Label("<html><font face=Serif size=-1>HTML footers can also have<br>dates: <b>|||EEE, MMM d, yyyy|||</b><br>and page numbers: <u>Page ###</u></font></html>"));
            componentPrinter1.setRightFooter(new J2Label("<html><font face=Serif size=-1>Note header & footer styles<br>can be BOX, LINE (as in<br>this case), or NONE</font></html>"));
            componentPrinter1.setComponent(comp1); // print contents of our main window


// J2FlowPrinter test ------------------------------------------------------------------
// make J2FlowPrinter instance flowPrinter1
        status.setText("make flowPrinter 1 (multiple Flowables)");
        final J2FlowPrinter flowPrinter1 = new J2FlowPrinter();

        // first Flowable: a J2TextPane instance
        JTextPane paneA = J2TextPrinter.makeHTMLPane("<html><center>"
              + "<b><font face=Serif size=+1>J2FlowPrinter Test</font>"
              + "<br><font face=Serif size=+0>Wildcrest Associates</font></b>"
              + "<br><font face=Monospaced size=-1>http://www.wildcrest.com</font></tt>"
              + "<br><font face=SansSerif size=-1>January 6, 2009"
              + "<font face=SansSerif size=-3><br>&nbsp;</font>"
              + "<hr align=center width=50%>"
              + "<font face=SansSerif size=-3><br>&nbsp;</font>"

              + "<i>Abstract</i></center>"
              + "<font size=-2>This document demonstrates the use of the J2FlowPrinter "
              + "printing component from Wildcrest Associates.  "
              + "It will assemble into one Pageable a series of "
              + "J2PrinterWorks printing components including "
              + "J2TextPrinter, J2TablePrinter, "
              + "J2TreePrinter, J2ListPrinter, J2ComponentPrinter, J2PanelPrinter, J2FlowPrinter itself, "
              + "VerticalGap, HorizontalLine, and PageEject, all printed back-to-back down the page.<br></font>"

              + "<br><font face=Serif size=+0><b>Flowables</b></font>"
              + "<br><font face=Serif size=-1>In addition to supporting "
              + "Pageables, J2PrinterWorks introduces a new printing "
              + "interface called Flowable.  Like Pageables, Flowables are printable components that may span "
              + "multiple pages, but unlike Pageables, they can also begin and end in the middle of any page.  "
              + "A J2FlowPrinter instance can group together any number of Flowable instances, "
              + "one immediately following after another back-to-back down the page.  "
              + "The resulting J2FlowPrinter instance is a Pageable (i.e., beginning and ending on page boundaries) "
              + "which represents a packed-together series of Flowables."
              + "<p>The following Flowables are provided by J2PrinterWorks:"
              + "<ul compact type=circle>"
              + "<li><b>J2TextPrinter</b> - a Flowable (as well as a Pageable) for JTextPane, JEditorPane, and JTextArea</li>"
              + "<li><b>J2TablePrinter</b> - a Flowable (as well as a Pageable) for JTable</li>"
              + "<li><b>J2TreePrinter</b> - a Flowable (as well as a Pageable) for JTree</li>"
              + "<li><b>J2ListPrinter</b> - a Flowable (as well as a Pageable) for JList</li>"
              + "<li><b>J2PanelPrinter</b> - a Flowable (as well as a Pageable) for JPanel</li>"
              + "<li><b>J2ComponentPrinter</b> - a Flowable (as well as a Pageable) for any Component</li>"
              + "<li><b>J2FlowPrinter</b> - is itself a Flowable as well as a Pageable (i.e., can nest J2FlowPrinters)</li>"
              + "<li><b>HorizontalLine</b> - a Flowable for drawing a horizontal line between successive Flowables</li>"
              + "<li><b>VerticalGap</b> - a Flowable for leaving a gap between successive Flowables</li>"
              + "<li><b>PageEject</b> - a Flowable for starting the next Flowable at the top of a new page</li></ul>"
              + "In addition you can create and intermix your own custom "
              + "com.wildcrest.j2printerworks.Flowable instances.  "
              + "By stringing together a series of Flowables, "
              + "you can do things like include a JTable, a JTree, a JList, a Component, "
              + "a Graphics drawing, an Image, or the content pane of a program window "
              + "within an overall text document made up of JTextPane instances, "
              + "placing titles above and captions below any of these components.  "
              + "Each Flowable can be horizontally justified left, right, or center within the page, "
              + "and vertically justified top, bottom, or center within the remaining space in the page."

              + "<br><br>Up to this point, the first Flowable in our J2FlowPrinter instance "
              + "is a single J2TextPrinter instance specified using HTML.  Next, we will add a "
              + "VerticalGap instance with a \"test gap\" of 1 inch to ensure that our subsequent J2FlowPrinter Examples "
              + "section doesn't start with a single line at the bottom of the first page.</font></html>"
        );

        status.setText("make textPrinter A");
        J2TextPrinter textPrinterA = new J2TextPrinter(paneA);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneA);
          textPrinterA.setDirectPrint(true);
        }

        // second Flowable is a VerticalGap instance

        // third Flowable: a J2TextPrinter instance
        JTextPane paneB = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=+0><b>J2FlowPrinter Examples</b></font>"
              + "<br><font face=Serif size=-1>After that PageEject, this is our third Flowable, another J2TextPrinter instance specified using HTML.  "
              + "In order to leave a little space between this text and the following J2TablePrinter instance, "
              + "our fourth flowable will be a VerticalGap instance, then we will "
              + "use a J2TablePrinter instance as our fifth Flowable.</font></html>"
        );
        status.setText("make textPrinter B");
        J2TextPrinter textPrinterB = new J2TextPrinter(paneB);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneB);
          textPrinterB.setDirectPrint(true);
        }

        // fourth Flowable is a VerticalGap instance

        // fifth Flowable: a J2TablePrinter instance
        String[] columnsA = {"Company Name", "Symbol", "99 Rank", "97 Rank", "Revenues"};
        Object[][] dataA = {
            {"General Motors", "GM", "1", "1", "$161,315"},
            {"Ford Motor", "F", "2", "2", "$144,416"},
            {"Wal-Mart", "WMT", "3", "4", "$139,208"},
            {"ExxonMobil", "XOM", "4", "3", "$100,697"},
            {"General Electric", "GE", "5", "5", "$100,469"},
            {"IBM", "IBM", "6", "6", "$81,667"},
            {"Citigroup", "C", "7", "40", "$76,431"},
            {"Philip Morris", "MO", "8", "10", "$57,813"},
            {"Boeing", "BA", "9", "36", "$56,154"},
            {"AT&T", "T", "10", "7", "$53,587"}};

        // test zero-size: columnsA = new String[0];
        // test zero-size: Object [][] dataA = { };

        JTable tableA = new JTable(dataA, columnsA);
        tableA.getColumnModel().getColumn(0).setPreferredWidth(125);
        tableA.getColumnModel().getColumn(1).setPreferredWidth(75);
        tableA.getColumnModel().getColumn(2).setPreferredWidth(65);
        tableA.getColumnModel().getColumn(3).setPreferredWidth(65);
        tableA.getColumnModel().getColumn(4).setPreferredWidth(100);
        tableA.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        status.setText("make tablePrinter A");
        J2TablePrinter tablePrinterA = new J2TablePrinter(tableA);
        tablePrinterA.setColumnHeaderPrinting(J2TablePrinter.NONE);

        if (onScreen) {
            JFrame tableFrameA = new JFrame("J2TablePrinter Flowable test");
            JScrollPane jspA = new JScrollPane(tableA);
            tableFrameA.getContentPane().add(jspA);
            tableFrameA.pack(); // first pack gets table sized
            int w = tableA.getSize().width+3;
            int h = tableA.getSize().height + tableA.getTableHeader().getHeight()+3;
            jspA.setPreferredSize(new Dimension(w,h));
            tableFrameA.pack(); // second pack fits frame to scrollpane
            tableFrameA.setLocation(170,170);
            tableFrameA.setVisible(true);
        }

        // sixth Flowable: a J2PanelPrinter instance
        J2Label labelA = new J2Label("<html>"
              + "<p><font face=SansSerif size=-2>This caption is the sixth Flowable, a J2PanelPrinter "
              + "instance used to print<br>a J2Label <i>defined using HTML</i>.  "
              + "Such a caption "
              + "could also be done using a<br><b>JTextPane in a J2PanelPrinter instance</b> "
              + "or using a <b>J2TextPrinter instance</b>.</font></html>"
        );

        // alternatively, instead of forcing line breaks with <br>,
        // you can specify preferred width and get J2Label to wrap itself,
        // but due to Java bug this has to be displayed on screen for this to work
        //JLabel labelA = new J2Label("<html>"
        //      + "<p><font face=SansSerif size=-2>This is the sixth Flowable, a J2PanelPrinter "
        //      + "instance used to print a J2Label instance <i>defined using HTML</i>.  "
        //      + "Such a caption "
        //      + "could also be done using a<br><b>JTextPane in a J2PanelPrinter instance</b> "
        //      + "or using a <b>J2TextPrinter instance</b>.</font></html>"
        //);
        //labelA.setPreferredSize(new Dimension(400,50));   // use this to control width, pack will cause layout

        status.setText("make panelPrinter A");
        J2PanelPrinter panelPrinterA = new J2PanelPrinter(labelA);

        if (onScreen) {
            JFrame panelFrameA = new JFrame("J2PanelPrinter JLabel test");
            panelFrameA.getContentPane().add(labelA);
            panelFrameA.pack();
            panelFrameA.setLocation(190,190);
            panelFrameA.setVisible(true);
        }

        // seventh Flowable is a VerticalGap instance

        // eighth Flowable: a J2TextPrinter instance
        JTextPane paneC = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>Right before this text was our seventh Flowable, another VerticalGap.  "
              + "This text is our eighth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will now show how a drawing might be inserted in our document.  "
              + "The figure is drawn using the paint method of a JPanel using "
              + "standard Java Graphics or Graphics2D drawing methods.  "
              + "The resulting JPanel is printed using a J2PanelPrinter, giving us our ninth Flowable.</font></html>");
        status.setText("make textPrinter C");
        J2TextPrinter textPrinterC = new J2TextPrinter(paneC);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneC);
          textPrinterC.setDirectPrint(true);
        }


        // ninth Flowable: a J2PanelPrinter instance
        JPanel drawing = new JPanel() {
            public Dimension getPreferredSize() { return new Dimension(180,150); }
            public void paint(Graphics g) {
                g.setColor(Color.white); Rectangle r = getBounds(); g.fillRect(r.x, r.y, r.width, r.height);
                g.setColor(Color.black); g.drawLine(25,135,25,25); g.drawLine(15,125,175,125);
                g.setColor(Color.yellow); g.fillRect(50,100,25,25);
                g.setColor(Color.black); g.drawRect(50,100,25,25);
                g.setColor(Color.pink); g.fillRect(85,75,25,50);
                g.setColor(Color.black); g.drawRect(85,75,25,50);
                g.setColor(Color.orange); g.fillRect(120,50,25,75);
                g.setColor(Color.black); g.drawRect(120,50,25,75);
                g.setFont(new Font("SansSerif",Font.PLAIN,10));
                g.drawString("Graphics",40,95);
                g.drawString("in a",90,70);
                g.drawString("JPanel",115,45);
            }
        };
        status.setText("make panelPrinter B");
        J2PanelPrinter panelPrinterB = new J2PanelPrinter(drawing);

        if (onScreen) {
            JFrame panelFrameB = new JFrame("J2PanelPrinter drawing test");
            panelFrameB.getContentPane().add(drawing);
            panelFrameB.pack();
            panelFrameB.setLocation(210,210);
            panelFrameB.setVisible(true);
        }

        // tenth Flowable: a J2TextPrinter instance
        JTextPane paneD = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This text is our tenth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will next show how an Image might be inserted in our document.  "
              + "The Image will be drawn using the Image constructor of J2PanelPrinter."
              + "This will be our eleventh Flowable.  If the Image is big, it will jump "
              + "to the top of the next page if it doesn't fit here.  This J2PanelPrinter is in \"shrink-to-fit\""
              + "mode and will choose between the remainder of this page or the next full page based "
              + "on which requires the least scaling.  Alternatively, you could print this J2PanelPrinter "
              + "instance using \"tile\""
              + "mode which would keep the Image unscaled and break it as necessary across multiple pages horizontally "
              + "and vertically.</font></html>");
        status.setText("make textPrinter D");
        J2TextPrinter textPrinterD = new J2TextPrinter(paneD);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneD);
          textPrinterD.setDirectPrint(true);
        }

        // eleventh Flowable: a J2PanelPrinter instance
        status.setText("make panelPrinter C");
        J2PanelPrinter panelPrinterC = new J2PanelPrinter(new ImageIcon("images"+File.separator+"CraterLake.jpg").getImage());
        //default is SHRINK_TO_FIT, or can test: panelPrinterC.setPageRule(J2PanelPrinter.TILE);
        //with TILE can also test: panelPrinterC.setMaximumPaginationGap(0.8);

        // twelfth Flowable: a J2TextPrinter instance
        JTextPane paneE = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This text is our twelfth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will now create a small JTree file directory and print it as a Flowable using a J2TreePrinter instance "
              + "which will be our thirteenth Flowable.</font></html>");
        status.setText("make textPrinter E");
        J2TextPrinter textPrinterE = new J2TextPrinter(paneE);
        if (System.getProperty("java.version").startsWith("1.2")) {
         if (onScreen) realize(paneE);
         textPrinterE.setDirectPrint(true);
       }

        // thirteenth Flowable: a J2TreePrinter instance
        JTree treeA = makeFileTree("."+File.separator+"images");
        expandAllRows(treeA, 75);
        status.setText("make treePrinter A");
        final J2TreePrinter treePrinterA = new J2TreePrinter(treeA);

        // fourteenth Flowable: a J2TextPrinter instance
        JTextPane paneF = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This text is our fourteenth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will now show a series of HorizontalLine instances, which may be used to separate "
              + "sections of a document.  We can specify horizontal line heights, the gap above and below "
              + "the horizontal line, the color of the horizontal line, and the width of the horizontal line, "
              + "either as a absolute width or a distance in from the page margins."
              + "The following are some examples:</font></html>");
        status.setText("make textPrinter F");
        J2TextPrinter textPrinterF = new J2TextPrinter(paneF);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneF);
          textPrinterF.setDirectPrint(true);
        }


        // fifteenth Flowable: a nested J2FlowPrinter instance with alternating HorizontalLines and J2PanelPrinters for J2Labels
        J2FlowPrinter flowPrinter2 = new J2FlowPrinter();
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This is the default HorizontalLine, which is 4 pixels high and "
            + "inset 1 inch from<br>left & right margins, has 16 pixels above and below, and is light gray:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine());
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 1 pixel high "
            + "and 100 pixels wide,<br>has 0 pixels above and below, and is blue:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(0,1,100,Color.blue));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 10 pixels high "
            + "and 375 pixels wide,<br>has 5 pixels above and below, and is green:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(5,10,375,Color.green));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 5 pixels high "
            + "and 5 pixels wide,<br>has 10 pixels above and below, and is red:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(10,5,5,Color.red));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 20 pixels high "
            + "and 200 pixels wide,<br>has 5 pixels above and below, and is orange:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(5,20,200,Color.orange));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 50 pixels high "
            + "and 1 pixel wide,<br>has 0 pixels above and below, and is dark gray:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(0,50,1,Color.darkGray));

        // sixteenth Flowable: a J2TextPrinter instance
        JTextPane paneG = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This J2TextPrinter is our sixteenth Flowable.  "
              + "However, the HorizontalLine examples above were done with "
              + "6 HorizontalLine instances alternating with 6 J2PanelPrinter instances (printing "
              + "J2Labels).  All 12 of these Flowables were placed inside their own "
              + "J2FlowPrinter instance.  Since J2FlowPrinter is itself a Flowable, this J2FlowPrinter was "
              + "simply added to our overall J2FlowPrinter, showing that J2FlowPrinter instances "
              + "can be nested.  "
              + "The only restriction is that no Flowable instance can "
              + "appear twice in the same overall J2FlowPrinter instance.<br><br>"
              + "It is also possible to scale Flowables individually within a J2FlowPrinter.  "
              + "This can be particularly useful in getting tables or images to fit nicely as figures "
              + "within an overall text document.  For examples, here is an image we've seen earlier.  The "
              + "actual image is much wider than one page.  The previous time we saw this image scaled to "
              + "exactly one landscape page wide and printed on its own page.  This time we will scale it "
              + "down to half the width of a portrait page, as we might do for a image or graphic representing "
              + "a figure in a paper."
              + "</font></html>");
        status.setText("make textPrinter G");
        J2TextPrinter textPrinterG = new J2TextPrinter(paneG);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneG);
          textPrinterG.setDirectPrint(true);
        }

        // seventeenth Flowable: a J2PanelPrinter instance with an image
        Image imgH = new ImageIcon("images" + File.separator + "CraterLake.jpg").getImage();
        status.setText("make panelPrinter H");
        J2PanelPrinter panelPrinterH = new J2PanelPrinter(imgH);
        panelPrinterH.setScale(printer.getBodyWidth() / imgH.getWidth(frame) / 2.0);

        // eighteenth Flowable: a J2TextPrinter instance
        JTextPane paneI = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>Or, here is a JTable we've seen before.  Previously, this was "
              + "printed as a Pageable on its own landscape page and scaled to fit within exactly one page high and one page wide.  "
              + "This time we will print it on a portrait page and scale it to exactly one page wide with no "
              + "scaling to control the length so that the whole table can span vertically across more than "
              + "one page as necessary.  This is appropriate since this time we are including "
              + "it as a Flowable so that it prints "
              + "back-to-back with our other Flowable instances."
              + "</font></html>");
        status.setText("make textPrinter I");
        J2TextPrinter textPrinterI = new J2TextPrinter(paneI);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneI);
          textPrinterI.setDirectPrint(true);
        }

        // nineteenth Flowable: a J2TablePrinter instance
        // make J2TablePrinter instance tablePrinter3
               status.setText("make tablePrinter J");
               J2TablePrinter tablePrinterJ = new J2TablePrinter(table2);
               tablePrinterJ.setHorizontalPageRule(J2TablePrinter.SHRINK_TO_FIT);

        // twentieth Flowable: a J2TextPrinter instance
        JTextPane paneK = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1> This J2TextPrinter is now Flowable number twenty.  "
              + "We will now append some more earlier J2TablePrinter, "
              + "J2TreePrinter, J2ListPrinter, and J2PanelPrinter instances, each one used this time as a Flowable instead "
              + "of a Pageable so they print back-to-back.  "
              + "The last of these is the J2PanelPrinter that contained mulitple Swing components.   "
              + "We include four copies of this to test BREAK_ON_COMPONENTS mode.  "
              + "These are another eight Flowables, giving a total of twenty-eight Flowables "
              + "(with one of these containing 12 more Flowables) "
              + "in our overall J2FlowPrinter instance."
              + "</font></html>");
        status.setText("make textPrinter K");
        J2TextPrinter textPrinterK = new J2TextPrinter(paneK);
        if (System.getProperty("java.version").startsWith("1.2")) {
          if (onScreen) realize(paneK);
          textPrinterK.setDirectPrint(true);
        }

// Now string together all the Flowables
// Note same Flowable instance cannot appear twice in same J2FlowPrinter instance!
        status.setText("add Flowables");
        flowPrinter1.addFlowable(textPrinterA);
        flowPrinter1.addFlowable(new VerticalGap(1.0, 0.15));
        flowPrinter1.addFlowable(textPrinterB);
        //test: flowPrinter1.addFlowable(new VerticalGap(0.0));
        flowPrinter1.addFlowable(new VerticalGap());
        flowPrinter1.addFlowable(tablePrinterA);
        flowPrinter1.addFlowable(panelPrinterA);
        flowPrinter1.addFlowable(new VerticalGap());
        flowPrinter1.addFlowable(textPrinterC);

        /* test:
        String taText = "This is a test of the J2TextPrinter String constructor, which puts the string "
                                                   + "into a JTextPane and optionally lets you specify the Font.  "
                                                   + "Text will be wrapped to the width of the page, left justified, and black.  "
                                                   + "If no font is specified, the default is SansSerif, PLAIN, 12.";
        flowPrinter1.addFlowable(new J2TextPrinter(taText, new Font("Serif",Font.PLAIN,12)));
        flowPrinter1.addFlowable(new J2TextPrinter(taText));
        */

        flowPrinter1.addFlowable(panelPrinterB);
        flowPrinter1.addFlowable(textPrinterD);
        flowPrinter1.addFlowable(panelPrinterC);
        flowPrinter1.addFlowable(textPrinterE);
        flowPrinter1.addFlowable(treePrinterA);
        flowPrinter1.addFlowable(textPrinterF);
        flowPrinter1.addFlowable(flowPrinter2);
        flowPrinter1.addFlowable(textPrinterG);
        flowPrinter1.addFlowable(panelPrinterH);
        flowPrinter1.addFlowable(textPrinterI);
        flowPrinter1.addFlowable(tablePrinterJ);
        flowPrinter1.addFlowable(textPrinterK);
        flowPrinter1.addFlowable(panelPrinter2);
        flowPrinter1.addFlowable(tablePrinter1);
        flowPrinter1.addFlowable(treePrinter1);
        flowPrinter1.addFlowable(listPrinter1);
        // add multiple copies of the component panel to test BREAK_ON_COMPONENTS
        J2PanelPrinter pp1 = new J2PanelPrinter(componentPanel);
        pp1.setPageRule(J2PanelPrinter.BREAK_ON_COMPONENTS);
        flowPrinter1.addFlowable(pp1);
        J2PanelPrinter pp2 = new J2PanelPrinter(componentPanel);
        pp2.setPageRule(J2PanelPrinter.BREAK_ON_COMPONENTS);
        flowPrinter1.addFlowable(pp2);
        J2PanelPrinter pp3 = new J2PanelPrinter(componentPanel);
        pp3.setPageRule(J2PanelPrinter.BREAK_ON_COMPONENTS);
        flowPrinter1.addFlowable(pp3);
        J2PanelPrinter pp4 = new J2PanelPrinter(componentPanel);
        pp4.setPageRule(J2PanelPrinter.BREAK_ON_COMPONENTS);
        flowPrinter1.addFlowable(pp4);

        flowPrinter1.setLeftFooter(J2Printer.FIRST, new J2Label("<html><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></html>"));
        flowPrinter1.setCenterFooter(J2Printer.FIRST,new J2Label("<html><div align=center><font size=+1>J2PrinterWorks Test Application</html>"));
        flowPrinter1.setRightFooter(J2Printer.FIRST,new J2Label("<html><div align=right><font size=-1>|||EEE, MMM d, yyyy|||<br>|||hh:mm:ss a zzz|||</html>"));
        flowPrinter1.setFooterStyle(J2Printer.FIRST, J2Printer.NONE);

// Finally, add all the Pageables to create the overall printer document
        status.setText("add Pageables");

        printer.addPageable(textPrinter1);
        printer.addPageable(tablePrinter1);
        printer.addPageable(tablePrinter2);
        printer.addPageable(tablePrinter3);
        printer.addPageable(treePrinter1);
        printer.addPageable(listPrinter1);
        printer.addPageable(panelPrinter1);
        printer.addPageable(panelPrinter2);
        printer.addPageable(panelPrinter3);
        printer.addPageable(componentPrinter1);
        printer.addPageable(flowPrinter1);

        // test N-up printing: printer.setNup(-9);
        // test monochrome print preview dialog: printer.setPageImagesMonochrome(true);

        // test customizable toolbar:
        /*
        JToolBar jtb = printer.getPrintPreviewToolBar();
        Component[] comps = jtb.getComponents();

        JButton printButton = (JButton)comps[0];
        printButton.setText(""); // remove text from Print button

        JButton setupButton = (JButton)comps[2];
        setupButton.setIcon(null); // remove icon from Page Setup button

        comps[20].setVisible(false); // hide the Close button
        comps[19].setVisible(false); // hide the Separator before the Close button

        jtb.removeAll(); // remove all toolbar components
        for (int i=0; i<13; i++) jtb.add(comps[i]); // add back the first 13 components
        jtb.add(comps[15]); // add back the Zoom Out button
        jtb.add(comps[14]); // add back the scale factor JTextField
        jtb.add(comps[13]); // add back the Zoom In button (thus reversing order of Zoom Out and Zoom In)
        for (int i=16; i<21; i++) jtb.add(comps[i]); // add back remaining components
        */

/* test invoking print preview toolbar actions, e.g., set left margin to 2.0 and jump to page 10
        JButton myButton= new JButton("My Button");
        printer.getPrintPreviewToolBar().add(myButton);
        myButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                JTextField pageNumberTextField = (JTextField)printer.getPrintPreviewToolBar().getComponents()[7];
                printer.setLeftMargin(2.0);
                pageNumberTextField.setText("10");
                pageNumberTextField.postActionEvent(); // likewise, can call doClick() on buttons
            } } );
*/

        // test: printer.showPrintPreviewPageSetupButton(false);

// Set up listeners to main window controls (turn on/off sections of document)
        ActionListener cbAL = new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                printer.clearPageable();
                if (cbtext1.isSelected()) printer.addPageable(textPrinter1);
                if (cbtable1.isSelected()) printer.addPageable(tablePrinter1);
                if (cbtable2.isSelected()) printer.addPageable(tablePrinter2);
                if (cbtable3.isSelected()) printer.addPageable(tablePrinter3);
                if (cbtree1.isSelected()) printer.addPageable(treePrinter1);
                if (cblist1.isSelected()) printer.addPageable(listPrinter1);
                if (cbpanel1.isSelected()) printer.addPageable(panelPrinter1);
                if (cbpanel2.isSelected()) printer.addPageable(panelPrinter2);
                if (cbpanel3.isSelected()) printer.addPageable(panelPrinter3);
                if (cbcomponent1.isSelected()) printer.addPageable(componentPrinter1);
                if (cbflow1.isSelected()) printer.addPageable(flowPrinter1);
            }
        };

        cbtext1.addActionListener(cbAL);
        cbtable1.addActionListener(cbAL);
        cbtable2.addActionListener(cbAL);
        cbtable3.addActionListener(cbAL);
        cbtree1.addActionListener(cbAL);
        cbpanel1.addActionListener(cbAL);
        cbpanel2.addActionListener(cbAL);
        cbpanel3.addActionListener(cbAL);
        cbcomponent1.addActionListener(cbAL);
        cbflow1.addActionListener(cbAL);

        printpreview.setEnabled(true);
        print.setEnabled(true);
        pagesetup.setEnabled(true);
        detailedpagesetup.setEnabled(true);
        frame.toFront();
        printer.setBusyCursor(frame, false);
        status.setText("Ready");

   }

   private static void realize(JTextPane pane) {
     JFrame f = new JFrame();
     f.getContentPane().add(pane);
     f.setSize((int)printer.getBodyWidth(),100);
     f.setLocation(100,100);
     f.setVisible(true);
   }

// make JTree for file directories
	static JTree makeFileTree(String path) {
	    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("root");
	    File f = new File(path);
	    if (path.equals(".")) f = new File(new File(new File(new File(path).list()[0]).getAbsolutePath()).getParent());
	    buildTree(rootNode,f.getParent(),f.getName()); // recursive
	    return new JTree(rootNode.getFirstChild());
	}

	static void buildTree(DefaultMutableTreeNode node, String path, String file) {
	    path = path+File.separator+file;
	    File f = new File(path);
	    if (f.isDirectory()) {
	        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(file);
	        String[] files = f.list();
	        for (int i=0; i<files.length; i++) buildTree(newNode, path, files[i]);
	        node.add(newNode);
	    }
	    else node.add(new DefaultMutableTreeNode(file, false));
	}

	static void expandAllRows(JTree tree, int enough) {     // brute force JTree row expander
	    while (true) {
	        int rowCount = tree.getRowCount();
                if (rowCount>=enough) break;
	        for (int i=0; i<rowCount; i++) tree.expandRow(i);
                if (rowCount==tree.getRowCount()) break;
	    }
	}


// make random JTables (not used above but available for testing)
    static Random rand = new Random(12345);
    static int tableNo=0;
    static JTable randomTable(int nRows, int nCols) {
        tableNo++;
        String[] columns = new String[nCols];
        for (int i=0; i<nCols; i++) columns[i] = new String("Col"+(i+1));
        Object[][] data = new Object[nRows][nCols];
        for (int i=0; i<nRows; i++) {
            data[i][0] = new String("Row"+(i+1));
            for (int j=1; j<nCols; j++)
                data[i][j] = new String("Table"+tableNo+ " " + rand.nextInt());
        }
        data[0][0] = new String(nRows+" X "+nCols);
        JTable jt = new JTable(data,columns);
        JFrame f = new JFrame();
        f.getContentPane().add(new JScrollPane(jt), "Center");   // construct table
        // to randomize: int width = 100+(int)(rand.nextDouble()*nCols*150);
        int width = 100+nCols*75;
        jt.setPreferredSize(new Dimension(width,16*nRows));
        jt.setBounds(0,0,width,16*nRows);

        f.pack();   // required
        f.setBounds(0,0,width,16*nRows);
        //f.setVisible(true);
        return jt;
    }



    static class MyPrintingEventHandler extends PrintingEventHandler {
      int totalPages;

      public void printingStart() {
        showProgress("About to print..."); // printing started
        totalPages = printer.getPageable().getNumberOfPages(); // expensive so do only once
      }

      public void printingDone() {
        windowToFront(); // needed prior to JDK 1.4.1 due to Bug Parade #4514422
        brieflyShowProgress("Printing done");
      }

      public void printDialogOK() {
        windowToFront(); // needed prior to JDK 1.4.1 due to Bug Parade #4514422
        showProgress("Start printing...");
      }

      public void printDialogCanceled() {
        windowToFront(); // needed prior to JDK 1.4.1 due to Bug Parade #4514422
        brieflyShowProgress("Printing canceled");
      }

      public void pageSetupDialogOK() {
         brieflyShowProgress("Page setup dialog OK");
      }

      public void pageSetupDialogCanceled() {
         brieflyShowProgress("Page setup dialog canceled");
      }

      public void pageStart(int pageNum) {
        showProgress("Printing page " + pageNum + " of " + totalPages);
      }

      public void pageDone(int pageNum) {
      }

      public void exceptionThrown(Exception e) {
        showProgress("Got an exception: " + e);
      }

    }

    static private void showProgress(String str) { status.setText(str); }

    static private void brieflyShowProgress(String str) {
      showProgress(str);
      new Thread() { public void run() {
          try { sleep(1500); } catch (Exception e) {}
          SwingUtilities.invokeLater( new Runnable() { public void run() { showProgress(" "); } } );
      } }.start(); // from different thread, wait 1 sec then erase
    }

    static void windowToFront() { frame.toFront(); } // needed under JDK 1.4 due to Bug Parade #4514422

}



