logo
  J2TextPrinter
logo

Overview

J2TextPrinter is a Java 2 component for printing multi-page JTextPane documents.  J2TextPrinter converts any JTextPane, JEditorPane, or JTextArea or subclass thereof into a Java Pageable, suitable for printing by the J2Printer class (or any Java PrinterJob).  It also implements Flowable for any JTextPane, JEditorPane, or JTextArea, so that it may be used with J2FlowPrinter as part of a series of Flowables printed back-to-back.

JTextPane, JEditorPane, and JTextArea are all subclasses of JTextComponent.  JTextArea is used in Java to represent simple, multiple-line text in a single font.  JEditorPane and.JTextPane are used in Java to represent rich text documents, including multiple fonts, sizes, styles, colors, alignment, spacing, tabs, indentation, etc.  In addition, JTextPane is also capable of including embedded graphics and embedded components into the middle of a rich text document. 

Since JTextPane is the most general JTextComponent supported by J2TextPrinter, we focus on it as the primary Java component used for forming and printing text documents. The underlying model (data) for a JTextPane is an instance of the class StyledDocument.  It can be formed programmatically by calling JTextPane methods, or by reading in HTML or RTF formatted data (subject to the limits of Java's support for these standards).

J2TextPrinter Pagination

When J2TextPrinter paginates the JTextPane, it breaks pages vertically on line boundaries.  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 to cause the printed pages to retain the exact line layout of the specified JTextPane.  J2TextPrinter then calculates the maximum number of lines that will fit in the remaining space on each page.  If a \f (form feed) character is encountered before this point, then a page break is inserted and the entire line containing the form feed character is omitted.  If the calculated page break occurs in the middle of an HTML table, the exact boundary between HTML table rows is determined for the actual page break. 

J2TextPrinter never paginates horizontally.  If the JTextPane contains content that is too wide for the page (such as a wide image, a wide row of an HTML table, or too much text on a line in WYSIWYG mode), it will automatically shrink-to-fit the entire JTextPane so that the widest content fits the available page width.

If an embedded graphic (e.g. image), embedded component, HTML table row, or very large font character is encountered that does not fit in the remaining space on the page, special fallback heuristics are used to determine where to break the page.  First, J2TextPrinter reads the value of getMaximumPaginationGap(), default is 0.2 or 20%.  If the remaining space on the page is less than that percentage of the full page height, then printing skips to the top of the next page (this controls the largest amount of space that will be wasted in order to keep content intact).  If this occurs within an HTML table row, the characters are examined one at a time to find a possible break between text lines that does fit (this handles the case of many lines of text within a single HTML table cell).  Otherwise, the graphic, component, HTML table row, or large character is sliced like an image ("tiled") at the exact pixel boundary that exactly fits the remaining space on the page.

Programming

You will find it useful to begin your program with:

    import com.wildcrest.j2printerworks.*;

rather than spell out the full package name for all J2PrinterWorks classes.

J2TextPrinter can instantiated with a zero-argument constructor:

    J2TextPrinter textPrinter = new J2TextPrinter();  

This creates a J2TextPrinter object and initializes all values to their defaults (see the J2TextPrinter Javadoc documentation ).  To specify the JTextPane to be printed by J2TextPrinter, you can use the J2TextPrinter method setPane:
    textPrinter.setPane(yourJTextPane);
At this point textPrinter is a Pageable suitable for printing by J2Printer.  Alternatively, you can use the single-argument J2TextPrinter constructor:

    J2TextPrinter textPrinter = new J2TextPrinter(yourJTextPane);

Note: if you change the contents of your JTextPane after printing, you need to call setPane (or instantiate a new J2TextPrinter) again before the next time you print for these changes to be recognized.

The following is a simple but complete Java program (J2TextPrinterSimplestTest.java ) that displays and prints a JTextPane using J2TextPrinter:

import javax.swing.*;
import com.wildcrest.j2printerworks.*;

class J2TextPrinterSimplestTest{
    static public void main(String args[]){
        JTextPane pane = new JTextPane();
        pane.setContentType("text/html");
        pane.setText("<html>Here are two lines of <b>HTML text</b>"
                   + "<br>that <i>will</i> be printed</html>");

        JFrame frame = new JFrame("J2TextPrinter test");
        frame.getContentPane().add(pane);
        frame.setSize(300,200);
        frame.setVisible(true);

        J2Printer printer = new J2Printer();
        printer.setSeparatePrintThread(false);
        J2TextPrinter textPrinter = new J2TextPrinter(pane);
        printer.addPageable(textPrinter);
        printer.print();

        System.exit(0);
    }
}

Most of the methods of J2TextPrinter are set and get methods for controlling its property values. The full list of J2TextPrinter methods, what they do, and their default values are given in the J2TextPrinter Javadoc documentation .
 

General properties

Whether J2TextPrinter is used as a Pageable or a Flowable, you can control whether it is left, right, or center justified horizontally on a page, top, bottom, or center justified within the remaing space on a page, whether the JTextPane reflows (rewraps) into the available page width or maintains its current layout ("WYSIWYG"), and how copying (cloning) of your JTextPane is used when printing.
 

Horizontal and vertical alignment

J2TextPrinter will print your JTextPane either top, center, or bottom justified vertically on the page between the gap below the header and gap above the footer.  The default value for vertical alignment is TOP.

Similarly, J2TextPrinter will print your JTextPane either left, center, or right justified horizontally on the page between the left and right margins.  The default value for horizontal alignment is CENTER.  However, note that if you have setWYSIWYG(false), which is the default, the JTextPane content rewraps to the width of the page by setting the width of the JTextPane the same as the page, so that the setHorizonalAlignment method has no real effect.  To use setHorizontalAlignement effectively, call setWYSIWYG(true) and set the width of the JTextPane as desired.

setHorizontalAlignment (int horizontalAlignment)
          Sets the horizontal alignment (LEFT, CENTER, RIGHT) for printing the JTextPane on the page, typically used with setWYSIWYG(true).

setVerticalAlignment (int verticalAlignment)
          Sets the vertical alignment (TOP, CENTER, BOTTOM) for printing the JTextPane on the page.
 

WYSIWYG support

J2TextPrinter normally reflows (line wraps) your document to fill the printed page.  However, J2TextPrinter can be controlled to print your JTextPane with exactly the same flow as it appears on the screen.  For this purpose J2TextPrinter has a setWYSIWYG method:

setWYSIWYG (boolean wysiwyg)
          Sets whether WYSIWYG (What-You-See-Is-What-You-Get) mode is enabled, thereby preventing relayout of text.

If you call setWYSIWYG(true), then you will get a WYSIWYG representation of your JTextPane with its line layout preserved as is.  If the width of your JTextPane is wider than the available printing width of the page (between margins), then J2TextPrinter will perform a shrink-to-fit operation on your JTextPane to make it fit.  J2TextPrinter never prints more than one page horizontally.
 

Cloning support

J2TextPrinter needs to be able to reflow (rewrap) the contents of your JTextPane without affecting its appearance on the screen. To accomplish this, J2TextPrinter prints your JTextPane's contents from its own internal JTextPane rather than from your JTextPane.

If cloningUsed is false, the StyledDocument of J2TextPrinter's internal JTextPane is set to your JTextPane's StyledDocument directly. If cloningUsed is true, the StyledDocument of J2TextPrinter's internal JTextPane is set to a clone (a copy created by Java serialization) of the StyledDocument of your JTextPane.

It has been determined that cloning is necessary if your JTextPane has inserted Components and appears in a Frame, JFrame, or JInternalFrame, or if it has inserted Components and you are printing from a separate thread (setSeparatePrintThread(true), which is the default). This is because Java doesn't support having the same component in multiple simultaneous views.

Since cloning is generally a safe, the default for cloningUsed is true. This method allows cloning to be disabled if it causes any problems for a particular JTextPane and since it is a bit slower.  This can occur in particular if the JTextPane contains embedded components that are for whatever reason not serializable.  There are also certain known serialization bugs in Java.  For example, under JDK 1.4.x certain HTML tags such as the "border" subtag of "table" are lost during serialization, so it turns out cloningUsed needs to be set to false for HTML documents that contain tables with borders, otherwise the borders will be missing.  This problem is fixed under JDK 1.5.

setCloningUsed (boolean cloningUsed)
          Specify whether cloning is to be used when printing with J2TextPrinter.

clone (javax.swing.JTextPane pane)
          Convenience factory method for making a new JTextPane whose StyledDocument is a serialized copy (deep clone) of the specified JTextPane's StyledDocument.
 
If your JTextPane does not have embedded images or embedded components, then you can clone your JTextPane without resorting to serialization using the following code:
  JTextPane text2 = new JTextPane();
  text2.setContentType(text1.getContentType());
  text2.setText(text1.getText());
  text2.setSize(text2.getPreferredSize()); // size required
The same technique can always be used for JEditorPane, which does not support embedded images or embedded components.


Pageable properties

When J2TextPrinter is used as a Pageable, it can have its own ("local") headers & footers (left, center, and right), margins (left, right, top, and bottom), and orientation (portrait & landscape).  Headers and footers can be specified as a String or a JLabel, can be different on the first page vs. the rest of the pages of the Pageable, and can include date, time, and page numbering.  The methods are the same as the parallel set of methods described in the J2Printer section under "Pageable properties".

If "local" values are not specified for this J2TextPrinter instance, the "global" (overall, default) values set using the parallel J2Printer methods will be used.  You can force the J2Printer "global" values to be used by calling the J2TextPrinter method(s) with the argument J2Printer.GLOBAL.

The Pageable properties of J2TextPrinter will be ignored when the J2TextPrinter is used as a Flowable.  This is because in this case the Pageable is the containing J2FlowPrinter, so page properties such as headers, footers, margins, and orientation will be controlled by the J2FlowPrinter Pageable, not the J2TextPrinter used as a Flowable. Note, however, that scaling does work for J2TextPrinter used as either a Pageable or a Flowable, that is, the J2TextPrinter can appear at its own scale within an overall J2FlowPrinter sequence.
 

Fit-to-page scaling

In addition to regular percentage scaling accomplished using the setScale method, J2TextPrinter also supports fit-to-page scaling when J2TextPrinter is used as a Pageable using the following method:

setMaximumPages (int pagesHigh)
          Set the maximum number of vertical pages for printing this J2TextPrinter (will minify to fit).

The setMaximumPages method causes your J2TextPrinter to be scaled down until it fits within pagesHigh pages vertically.  This method cannot be used to magnify and instead will only minify your J2TextPrinter (hence the name setMaximumPages).  The method works by starting at a scale of 1.0 and reducing the scale in increments of 0.005 (half a percent) until your J2TextPrinter fits within the prescribed limits (note that if setWYSIWYG(false) is in effect, the JTextPane will reflow as scaling progresses).  If pagesHigh is set to 0 or less, this is taken as a signal that the scaling is unconstrained, that is, the J2TextPrinter can use as many pages as it needs.

NOTE: This method causes the rescaling to take place at the time it is called and does NOT remember or maintain the page limits you specify. Thus, if you change any printing parameters including your document content, headers, footers, margins, paper size, orientation, etc., you must call setMaximumPages again.

The percentage and fit-to-page scaling features interact.  You can call setMaximumPages(pagesHigh) to specify a number of pages and then use the method getScale() to find out the resulting scaling factor.  Or you can call setScale(factor) to scale to a desired percentage and then use getNumberOfPages() to find out the number of resulting pages.  The getNumberOfPages() method works for both magnification and minification (thus you could use it to implement your own fit-to-page feature for magnification, which you might call setMinimumPages(pagesHigh) ).
 

Formatted text support

The underlying model of a JTextPane is an instances of the Java class StyledDocument.  You can specify the contents of your StyledDocument either directly by programming a StyledDocument directly or by using Java's HTML or RTF formatted text support.
 

Plain text

We begin by noting that you can set the contents of a JTextPane using plain text simply using the code:
    yourJTextPane.setContentType("text/plain");
    yourJTextPane.setText(yourString);
 

StyledDocument

At the other end of the spectrum, the contents of a JTextPane StyledDocument can be specified using the rich text editing methods defined in the JTextPane class.  These methods are the most general way to defined a StyledDocument but can be somewhat complex to use.  But since many text documents can be built up sequentially by adding text in a given font to the end of the document, we have provided two methods to simplify this style of document creation.

appendStyledText (javax.swing.JTextPane pane, java.lang.String string, javax.swing.text.SimpleAttributeSet attribute)
          Convenience method for appending text with the specified rich text attributes to the end of a JTextPane's StyledDocument.

makeSimpleAttributeSet (java.lang.String fontName, java.lang.String style, int size, int align, java.awt.Color color)
          Convenience factory method for making a SimpleAttributeSet for a desired Font, specified using font name, style, size, alignment, and color.  Having created this SimpleAttributeSet instance for a desired Font, you can optionally add numerous other text formatting attributes such as LineSpacing, TabSet, FirstLineIndent, etc. using commands like:
    StyleConstants.setLineSpacing(yourSimpleAttributeSet, spacing);
where spacing is a float giving the desired line spacing in "points", i.e. 1/72nds of an inch.

Writing a StyledDocument to a file
The following code can write out the contents of a JTextPane StyledDocument as a serialized object to a file:

      FileOutputStream fos = new FileOutputStream(fileName);
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(yourJTextPane.getStyledDocument());
      oos.flush();
      oos.close();

This is the same way the Java "StylePad" sample code reads and writes its files.  You can use this code to write out a StyledDocument whether it was defined using the JTextPane StyledDocument methods or by reading the docuement using HTML or RTF.

Reading a StyledDocument to a file
The following code can read back the contents of a JTextPane StyledDocument as a serialized object from a file:

    EditorKit editorKit = new StyledEditorKit();
    JTextPane1.setEditorKit(editorKit);
    FileInputStream fis = new FileInputStream(fileName);
    ObjectInputStream ois = new ObjectInputStream(fis);
    StyledDocument doc = (StyledDocument) ois.readObject();
    ois.close();
    yourJTextPane.setStyledDocument(doc);

Note that since Java has not yet solved the problem of reading serialized data between JDK versions, you can only read and write serialized documents created within the same JDK version.
 

HTML

JTextPane StyledDocuments can also be specified using HTML. While the results are not as general as creating a StyledDocument directly, it is generally easier and leverages the broad availability of HTML content and tools.

Important note:  The use of HTML is limited by Java's JTextPane implementation of HTML.  In general, you can only rely on basic HTML commands, and many subtags and options supported by common browsers such as Netscape or Internet Explorer are not supported by Java.  Enough basic HTML is implemented to be a useful tool in creating rich text documents, but you cannot always take the HTML you get from FrontPage, Netscape, or off the web and count on JTextPane to display it properly .  If you are using HTML, you first need to make sure you can get JTextPane to display your HTML, since J2TextPrinter can only print what JTextPane can display (try using J2TextPrinterTestApplication to read in HTML files).  Another common problem is that you can easily define HTML content such as tables or images that may display OK but are too wide for the printed page (in which case J2TextPrinter will perform a shrink-to-fit operation to make these portions of your HTML fit).

Specifying HTML in Java
You can specify a JTextPane using HTML using code like this:

    JTextPane pane = new JTextPane();
    pane.setContentType("text/html");
    pane.setText("<html>Here are two lines of <b>HTML text</b>
               + "<br>that <i>will</i> be printed</html>");

To make this common operation even easier, J2TextPrinter provides a convenience method makeHTMLPane that does the same thing:

makeHTMLPane (java.lang.String html)
          Convenience factory method for creating a JTextPane using an HTML String.

However, a big advantage to using the makeHTMLPane method is that it creates a JTextPane that uses
a special HTMLEditorKit which, unlike the default, uses synchronous loading of images,
so that images are assured to be loaded when this method returns.
   

Reading HTML from a file or URL
You can also set the contents of your JTextPane by reading HTML from a file or URL using code like the following:

    JTextPane1.setContentType("text/html");
    try {
        java.net.URL url = new File(fileName).toURL();
        yourJTextPane.setPage(url);
    } catch (Exception e) { e.printStackTrace(); }

Note that by default, setPage loads asynchronously.  That is, the setPage method returns immediately and the page contents proceeds to load in the background on a separate thread, which can take some amount of time if the HTML file is big, contains images, or loads some or all of its content from over a network, etc.  To make setPage load synchronously, so that the entire HTML page is loaded before setPage returns, replace the default HTMLEditorKit with one that forces synchronous loading, as follows:

    yourJTextPane.setEditorKit(new HTMLEditorKit() {
        public Document createDefaultDocument() {
            Document doc = super.createDefaultDocument();
            ( (HTMLDocument) doc).setAsynchronousLoadPriority( -1);
            return doc;
        }
    });

Using HTML Fonts
You can specify HTML fonts using the Java core Font names Serif, SansSerif, Monospaced, etc. or using the exact same names as the core fonts used by Java, which on Windows are: Times New Roman, Arial, Courier, Symbol, and WingDings.  For all other designations, Java will substitute Sans Serif and as a result, the "Variable Width" font setting common in HTML documents winds up as SansSerif instead of Serif as in the standard browsers.

HTML page size
With HTML it is possible to specify content that JTextPane is not able to paginate correctly.  In particular, large images and tables with widths specified in pixels can cause HTML documents to not fit within the available page width.  In this case, J2TextPrinter will shrink-to-fit your HTML content in order to fit.  J2TextPrinter will always shrink to fit one page wide since J2TextPrinter does not paginate horizontally.  To paginate vertically, J2TextPrinter will try to break HTML tables on row and/or column boundaries.  If less than getMaximumPaginationGap() of the page remains, it will skip to the top of the next whole page.  If more than this amount remains and the next row does not fit, it will search inside the row for reasonable text line boundary to break on, though this may not work across all columns of that row.  Failing that, it will cut though the table on pixel boundaries, like an image.
 

RTF

JTextPane StyledDocuments can also be specified using RTF, though Java's RTF support is more limited than either HTML or using StyledDocument directly.  In particular Java's RTF implementation does not support basic RTF features such as embedded graphics or tables.

Important note: Sun has not been responsive to improving its RTF support (see Bug Parade 4261277 ).  There are many postings on Sun's Bug Parade requesting that Sun address the problem of missing or broken RTF features, but Sun's responses are generally negative with statements like these:
    "The demand for RTF support seems to be low, so implementing this is a very low priority."
    "Unfortunately the RTF support in Swing is rather limited, and we don't plan on fixing this at this time."
It appears Java's support for RTF is very unlikely to improve over its current state, and we must advise you to either switch to the StyledDocument or HTML formats or restrict your use of RTF to just that narrow subset of features that presently work.

Reading RTF from a file
You can enter RTF programmatically using setContentType and setText as in the HTML example above, but RTF is not designed as a user format like HTML, so this is rarely done.  Usually a JTextPane is used to read in the RTF from a file using code like the following:
    JTextPane1.setContentType("text/rtf");
        try {
            java.net.URL url = new File(fileName).toURL();
            yourJTextPane.setPage(url);
        } catch (Exception e) { e.getPrintStackTrace()); 

Note that by default, setPage loads asynchronously.  To force setPage to load synchronously, use an RTFEditorKit modified in the manner shown above for HTMLEditorKit.

RTF graphics and tables
Sun's support for RTF does not include support for embedded graphics or tables, which are common in RTF documents.  However, once you have read an RTF document into a JTextPane, you can use the JTextPane methods embedIcon() or embedComponent() to insert an Image or drawing in JPanel at a position specified by the JTextPane method setCaretPosition().

Using RTF Fonts
Sun's RTF support will preserve most styles and sizes of fonts correctly but substitutes SansSerif font for anything that isn't exactly the same name as the core fonts used by Java, which on Windows are: Times New Roman, Arial, Courier, Symbol, and WingDings.
 

Additional features

Page breaks

J2TextPrinter supports explicit page breaks.  Simply insert a form feed character on its own line into your document at the point you wish to force a page break in your output.  For example, the simplest page break sequence can be specified in a Java String as:
    "\n\f\n" 
Note that is is a 3 character sequence (newline-form feed-newline), not a 6 character sequence with backslashes and letters.  If you wish to type a form feed character directly into your JTextPane, you will either need to use your system's ability to directly type in special characters (e.g., on some systems you can type Ctrl-L or use a special system accessory application or menu command).  Alternatively, you can copy and paste a form feed character from the following line (which looks like a space in Internet Explorer and like a small box, representing a "non-printing" character, in Netscape):

The entire line delimited by newline ("\n") characters surrounding the form feed  ("\f") character will be ignored in the printed output.  This feature is designed to let you represent a page break in your JTextPane using, for example the following sequence:
     "\n page break -------------- <\f> -------------- page break \n"
so users can see the page break in the JTextPane user interface but still have this entire line omitted from the printout.  

Note that you can even use form feed characters to indicate page breaks in HTML documents, even though HTML does not itself define a tag for page break.  However, since HTML regards all newline characters as "soft" carriage returns and rearranges them when rewrapping your text, you may inadvertantly wind up with good text on the same line as your form feed, resulting in it being unintentionally omitted.  To prevent this, use the following sequence to indicate a page break in an HTML document (if specified using a Java String)::
    "<p>\f<p>"
or alternatively
    "<p>&#12;<p>"
If you wish to type a page break directly into your HTML-based JTextPane, you either need to use your system's special character entry means (see above), or copy and paste the following line (again, the character between the <p> tags looks like a space in Internet Explorer and a small box in Netscape):
<p> <p>

However, you may find an easier alternative to using form feeds for page breaks within a single J2TextPrinter instance is to break your JTextPane into two JTextPanes on either side of the intended page break.  Then make separate J2TextPrinter instances for each JTextPane and use addPageable to add each of them to your J2Printer instance.  This will result in the page break since Pageables always start on page boundaries.  Alternatively you can use a J2FlowPrinter instance, using addFlowable to add the two J2TextPrinter instances separated by addFlowable(new PageBreak()).
 

Embedding images

You can use the standard Java JTextPane method insertIcon(Image) to embed .gif or .jpg images in your JTextPane, and J2TextPrinter will print them.  If the image does not fit within the remaining space on the current page, J2TextPrinter will print the image beginning on the next page, and if the image is bigger than a page then it will be skipped entirely since JTextPane has no provision for paginating or rescaling images that are bigger than a printed page.  Use J2PanelPrinter if you want to print large images either scaled to a page or tiled across multiple pages.

The insertIcon method will actually embed the Image within the flow of the text.  If you wish to place your Image on its own line, you can accomplish this using line breaks within the JTextPane before and after the insertIcon call, or alternatively you can intermix a J2PanelPrinter (with an Image) and J2TextPrinter instances using J2FlowPrinter.
 

Embedding components

You can use the standard Java JTextPane method insertComponent(Component) to embed any Component (or JComponent) instance in your JTextPane, and J2TextPrinter will print them.  This means you can embed JTable, JTextField, JTree, JList, JPanel, or even other JTextPane instances directly in your JTextPane document.  However, the same page size limitations apply as for embedding images. That is, if the component does not fit within the remaining space on the current page, J2TextPrinter will print the component beginning on the next page, and if the component is bigger than a page then it will be skipped entirely.  This capability is only useful for embedding components that are less than 1 page.  If you need to intermix multi-page JTables, JTrees, JLists, and/or other Components, use J2FlowPrinter to intermix J2TextPrinter, J2TablePrinter, J2TreePrinter, J2ListPrinter, and/or J2PanelPrinter instances.

The insertComponent method will actually embed the Component within the flow of the text.  If you wish to place your Component on its own line, you can accomplish this using line breaks before and after the insertComponent call, or alternatively you can intermix J2PanelPrinter (with a Component) and J2TextPrinter instances using J2FlowPrinter.

NOTE: Java's JTextPane insertComponent feature is fairly flaky (see Known Problems section), so we highly recommend using J2FlowPrinter to embed Components in your documents using J2PanelPrinter.

Embedding drawings

You can embed drawings created with Java Graphics or Graphics2D calls in your JTextPane document for printing with J2TextPrinter by using either the insertIcon(Image) or insertComponent(Component) methods of JTextPane.  As always, the rule of thumb is that anything you can display using a JTextPane, J2TextPrinter will print.

One way to embed graphics in your JTextPane is using insertIcon(Image) .  You first create a Java Image, then create a Graphics (or Graphics2D) context for it, then draw into the context using standard Java Graphics (or Graphics2D) methods, then use insertIcon(Image) to embed the image in your JTextPane.  Here is some sample code for this:

    Image img = new Image(300,300); // create 300 x 300 Image
    Graphics g = img.getGraphics(); // get Graphics context for image
    g.drawLine(50,50,200,200);      // draw into Image using drawLine,
    g.drawString("This is some graphics",150,100); // drawString, etc.
    int end = yourJTextPane.getStyledDocument().getLength(); // find end
    yourJTextPane.setCaretPosition(end); // position at end
    yourJTextPane.insertIcon(img);       // insert Image

An alternative way to embed graphics in your JTextPane is using insertComponent(Component).  You first create a JPanel subclass, override its paint method to draw your graphics, then use insertComponent(Component) to embed your JPanel in your JTextPane.  Here is some sample code to accomplish this (to keep the code compact, we use an inner class to define the JPanel subclass):

    JPanel jp = new JPanel() {  // define JPanel subclass
        public Dimension getPreferredSize() { return new Dimension(300,300); }
        public void paint(Graphics g) { // override paint method
            g.drawLine(50,50,200,200);  // draw using drawLine,
            g.drawString("This is some graphics",150,100); // drawString, etc.
        }
    };
    int end = yourJTextPane.getStyledDocument().getLength(); // find end
    yourJTextPane.setCaretPosition(end); // position at end
    yourJTextPane.insertComponent(jp);   // insert Component at end

As discussed above, embedded Images or Components cannot be broken over page boundaries, so an embedded Image or Component will shrink-to-fit in order to fit if over half a page is available is on the current page, or it will skip to the next whole page and shrink-to-fit there.
If you prefer to tile rather than shrink-to-fit oversize images or drawings, we recommend using a J2FlowPrinter and alternate J2TextPrinter instances containing your text with J2PanelPrinter instances containing your images or drawings.
 

Non-GUI JTextPane  printing

It is not necessary that the JTextPane you print be in a JFrame visible on the screen.  You may be printing from a server application and have no display.  Or you may have a JTextPane displayed but wish to print from a different non-GUI copy of the JTextPane.  The latter situation can arise when you want to set the printing JTextPane properties different from your on-screen JTextPane display, such as to force different document width and line wrap as appropriate for a printed page.  In such cases, you can use code like:

  JTextPane text2 = new JTextPane();
  text2.setContentType(text1.getContentType());
  text2.setStyledDocument(text1.getStyledDocument());
  text2.setSize(text2.getPreferredSize()); // size required

Or you can just use the J2TextPrinter convenience method:
    JTextPane text2 = J2TextPrinter.clone(text1);
 

Java Borders

You can use the Java Swing Borders classes to add various kinds of borders to your JTextPane, and these will be printed by J2TextPrinter.  Borders work when using J2TextPrinter as either a Pageable or a Flowable.  You can use both the Java Borders feature and the J2TextPrinter method setOutsideLines(true) at the same time (the Java Border will be inside the outside line).  Some examples of Java Borders are:
    yourJTextPane.setBorder(new LineBorder(Color.green,20));
   
yourJTextPane.setBorder(new EmptyBorder(10,10,10,10));
   
yourJTextPane.setBorder(new CompoundBorder(new LineBorder(Color.green,20),new EmptyBorder(10,10,10,10)));
Note that by default JTextPane has a 3 pixel EmptyBorder on all four sides.  Therefore, if you want to print J2TextPrinter instalces back-to-back and have uniform line spacing, you need to set each JTextPane border to an EmptyBorder with zero insets.

Freeing memory

The Java support for JTextPane imaging consumes relatively large amounts of memory, so that considerable memory space is allocated and held when printing large documents.  This can lead to problems especially when printing multiple times during the running of an application.  To facilitate garbage collection, J2PrinterWorks includes a dispose() method which explicitly releases all allocated storage and nulls all held references (similar to JFrame.dispose()).  Important: Do not call until printing completes, i.e., only after return from print if setSeparatePrintThread(false) or only after receive printing event + isPrinting() true if setSeparatePrintThread(true).

dispose()
        Release all resources held by this J2TextPrinter object, must not be called until printing completes.

The dispose() method also can be used to fix another problem.  If you have an embedded component in your JTextPane and you call setCloningUsed(false) because the embedded component is not cloneable (Serializable), then J2PrinterWorks calls setVisible(true) on the internal imaging JFrame it uses as part of printing (or print preview).  This causes a "printing..." tab to show up in the Windows task bar, which is not removed when printing completes.  Calling dispose() when printing completes will deallocate this JFrame and thereby cause the "printing..." tab to go away.


Direct print
A special situation arises for the case of a JTextPane defined using HTML which contains HTML <form> tags.  If the user enters values in the text fields, text areas, and other GUI elements of the HTML <form> and if it is desired to print the JTextPane including these user entries, it is necessary to use a special feature of J2TextPrinter called "direct print". 

setDirectPrint(boolean directPrint)
          Sets whether "direct print" mode is enabled.

Setting this value to "true" instructs J2TextPrinter to print the JTextPane directly from the JTextPane being presented to the user.  Cloning will not be used, regardless of the value set by the setCloningUsed method. WYSIWYG will be assumed, regardless of the value set by the setWYSIWYG method, and the JTextPane will not be reflowed. JTextPane must already be "realized", e.g., added to a frame followed by a call to either frame.setVisible(true) or frame.pack(). The JTextPane will only be paginated, with the current layout and line breaks unchanged. Printing will be scaled down as necessary to fit the available page width (if scaling is required, you must be using JDK 1.4.2_02 or later, see below). Direct print lacks any thread safety and the JTextPane must be left untouched until printing completes.

The main benefit of using direct print is that text entries in the fields of an HTML form will be printed, as well as any embedded components (even if they are not cloneable) without the need to remove them from the current display.

The direct print technique only works under JDK 1.3.1, 1.4.1, and 1.4.2 or later. It does not work at all prior to JDK 1.3.1. Under JDK 1.4,  there is a Java bug (Bug Parade 4708924) that causes any use of the direct print technique to throw an "IllegalStateException: constrain(xywh) is not supported for complex transform". In subsequent releases, the bug was partially fixed so that direct printing will work but prior to JDK 1.4.2_02 you can only perform a print preview with 100% scale and cannot have a JTextPane wider than getBodyWidth() which would cause the JTextPane to be scaled down.

"Widow" control
A "widow" is the first line of a new paragraph appearing by itself at the bottom of a page.  This doesn't look very good, particularly when the paragraph is indented, so you may wish to ensure that at least 2 lines of text from any given paragraph appear at the bottom of a page.  To eliminate "widows", break your JTextPane into multiple JTextPane instances, one per paragraph (you may also wish to set all the borders to an EmptyBorder with insets of zero).  Create J2TextPrinter instances for each JTextPane.  Now create a J2FlowPrinter and use addFlowable to alternate the J2TextPrinter instances with VerticalGap instances with a test gap of, say, 1.0 and a regular gap of 0.0.   Each VerticalGap will test whether there is less than one inch left on the page and if so, it will skip to the top of the next page, otherwise it will put in a gap of zero length and the following J2TextPrinter will have at least one inch to display multiple lines before the page break.

Using J2TextPrinter as a JavaBean

The J2PrinterWorks components are designed to work as a JavaBeans components in visual builder environments.  However, J2PrinterWorks uses APIs unique to the Java 2 system, and not all visual development environments currently work and build visually with Java 2-based components.  In particular, WebGain/Symantec Visual Cafe has to be specially configured to allow you to import the J2PrinterWorks components into its component library and exhibits other problems when using it visually. In contrast, Borland JBuilder and IBM VisualAge for Java can read and use J2PrinterWorks.jar as either a set of JavaBeans components or programmatically as classes.  See Installation & Compatibility for further details.  Since the J2PrinterWorks components are not GUI components per se, the need to work with them visually isn't great, so that using them programmatically isn't much of a limitation.

If J2PrinterWorks.jar is properly installed, you will see the J2PrinterWorks components, including J2Printer and J2TextPrinter, in the component palette of your visual programming environment.  Click on the J2Printer bean and drop it on your work area and likewise drag the J2TextPrinter bean and drop it on your work area.  You will see icons representing instances of these beans.  These will not be displayed at run-time.

When you bring up the property sheet for either component, you will be able to see and edit their properties.  The properties may be set as desired, with values as defined in the Javadoc documentation .

You can make the J2Printer bean print by using your visual programming environment to perform event wiring from an actionPerformed event such as a button push to the J2Printer "print" event target method.  You can use this to print the instance of J2TextPrinter, using the setPane method or the J2TextPrinter constructor itself to specify your JTextPane.

All the J2PrinterWorks properties are bound properties.  You may use your visual programming environment to do property-to-property binding in either direction between the J2PrinterWorks components and your other beans.  None of the J2PrinterWorks properties are constrained properties.  This frees you from having to place try...catch blocks around your "set" calls in regular programming.

J2PrinterWorks components are fully serializable.  After customizing any properties, instances can be saved along with any other beans to which they may be wired.  When reloaded, the instances will come back with their customized values.  No J2PrinterWorks properties are declared transient.


© Copyright 2009, Wildcrest Associates (http://www.wildcrest.com )