Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 22. (Special topics) Printing. Easy for reading, Click here!

Custom Search
Swing Chapter 22. (Special topics) Printing. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 4/5 



Previous Page Previous Page (3/5) - Next Page (5/5) Next Page
Subpages: 1. Java 2 Printing API overview
2. Printing images
3. Print preview
4. Printing styled text
5. Printing tables

22.4  Printing styled text

In this section we'll add printing capabilities to the RTF word processor application developed in chapter 20. The printing of styled text would be easy if JTextComponent or JTextPane implemented the Printable interface and provided the capability to print their content. Unfortunately this is not the case (at least as of Java 2 FCS). So we have to get fairly clever, and create our own BoxView subclass to specifically handle printing.

Our styled editor class will now implement the Printable interface and delegate the mechanics of printing of each page to our custom BoxView subclass. Note that this custom view is not actually displayed on the screen as the editor. It sits in the background and is used only for printing and display in our print preview component.

Recall, from our discussion in chapters 11 and 19, that styled documents consist of a hierarchy of elements: paragraphs, images, components, etc. Each element is rendered by an associated view, which are all children of the root view. A BoxView in particular, arranges all its child views along either the x or y axis (typically the y axis). So, in this example, when we need to render a page, we start from the first child view of our custom BoxView, and render each of the child views sequentially, placing each below the previous in the vertical direction. When the next page should be rendered, we start from the first remaining view and continue in this fashion until all child views have been rendered. (This process will be explained in greater detail below.)

Figure 22.5 Print preview showing a four page RTF document.

<<figure22-5.gif>>

The Code: WordProcessor.java

see \Chapter22\3

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import java.util.*;

import java.sql.*;

import java.awt.print.*;

import javax.swing.plaf.basic.*;

import javax.swing.*;

import javax.swing.text.*;

import javax.swing.event.*;

import javax.swing.border.*;

import javax.swing.text.rtf.*;

import javax.swing.undo.*;

import dl.*;

public class WordProcessor extends JFrame implements Printable

{

  // Unchanged code from section 20.9

  protected PrintView m_printView;

  // Unchanged code from section 20.9

  protected JMenuBar createMenuBar() {

    // Unchanged code from section 20.9

    mFile.addSeparator();

    Action actionPrint = new AbstractAction("Print...",

     new ImageIcon("print.gif")) {

      public void actionPerformed(ActionEvent e) {

        Thread runner = new Thread() {

          public void run() {

            printData();

          }

        };

        runner.start();

      }

    };

    item =  mFile.add(actionPrint); 

    item.setMnemonic('p');

    item = new JMenuItem("Print Preview");

    item.setMnemonic('v');

    ActionListener lstPreview = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        Thread runner = new Thread() {

          public void run() {

            setCursor(Cursor.getPredefinedCursor(

              Cursor.WAIT_CURSOR));

            new PrintPreview(WordProcessor.this);

            setCursor(Cursor.getPredefinedCursor(

              Cursor.DEFAULT_CURSOR));

          }

        };

        runner.start();

      }

    };

    item.addActionListener(lstPreview);

    mFile.add(item);

    mFile.addSeparator();

    // Unchanged code from section 20.9

  }

  // Unchanged code from section 20.9

  public void printData() {

    getJMenuBar().repaint();

    try {

      PrinterJob prnJob = PrinterJob.getPrinterJob();

      prnJob.setPrintable(this);

      if (!prnJob.printDialog())

        return;

      setCursor( Cursor.getPredefinedCursor(

        Cursor.WAIT_CURSOR));

      prnJob.print();

      setCursor( Cursor.getPredefinedCursor(

        Cursor.DEFAULT_CURSOR));

      JOptionPane.showMessageDialog(this,

        "Printing completed successfully", "Info",

        JOptionPane.INFORMATION_MESSAGE);

    }

    catch (PrinterException e) {

      e.printStackTrace();

      System.err.println("Printing error: "+e.toString());

    }

  }

  public int print(Graphics pg, PageFormat pageFormat,

   int pageIndex) throws PrinterException {

    pg.translate((int)pageFormat.getImageableX(),

      (int)pageFormat.getImageableY());

    int wPage = (int)pageFormat.getImageableWidth();

    int hPage = (int)pageFormat.getImageableHeight();

    pg.setClip(0, 0, wPage, hPage);

    // Only do this once per print

    if (m_printView == null) {

      BasicTextUI btui = (BasicTextUI)m_monitor.getUI();

      View root = btui.getRootView(m_monitor);

      m_printView = new PrintView(

        m_doc.getDefaultRootElement(),

        root, wPage, hPage);

    }

    boolean bContinue = m_printView.paintPage(pg,

      hPage, pageIndex);

    System.gc();

    if (bContinue)

      return PAGE_EXISTS;

    else {

      m_printView = null;

      return NO_SUCH_PAGE;

    }

  }

  public static void main(String argv[]) {

    new WordProcessor();

  }

  // Unchanged code from section 20.9

  class PrintView extends BoxView

  {

    protected int m_firstOnPage = 0;

    protected int m_lastOnPage = 0;

    protected int m_pageIndex = 0;

    public PrintView(Element elem, View root, int w, int h) {

      super(elem, Y_AXIS);

      setParent(root);

      setSize(w, h);

      layout(w, h);

    }

    public boolean paintPage(Graphics g, int hPage,

     int pageIndex) {

      if (pageIndex > m_pageIndex) {

        m_firstOnPage = m_lastOnPage + 1;

        if (m_firstOnPage >= getViewCount())

          return false;

        m_pageIndex = pageIndex;

      }

      int yMin = getOffset(Y_AXIS, m_firstOnPage);

      int yMax = yMin + hPage;

      Rectangle rc = new Rectangle();

      for (int k = m_firstOnPage; k < getViewCount(); k++) {

        rc.x = getOffset(X_AXIS, k);

        rc.y = getOffset(Y_AXIS, k);

        rc.width = getSpan(X_AXIS, k);

        rc.height = getSpan(Y_AXIS, k);

        if (rc.y+rc.height > yMax)

          break;

        m_lastOnPage = k;

        rc.y -= yMin;

        paintChild(g, rc, k);

      }

      return true;

    }

  }

// Remaining code is unchanged from section 20.9

Class WordProcessor

In comparison to the example of section 20.9, this class imports two new packages: java.awt.print and javax.swing.plaf.basic. The first one provides the necessary printing API, while the second is used to gain access to text component UI delegates (we will soon see why this is necessary).

One new instance variable, PrintView m_printView, represents our custom view used to print the styled document (see below). The createMenuBar() method now creates and adds to the "File" menu two new menu items titled "Print..." and "Print Preview". When the first one is selected it calls the printData() method, while the second one creates a PrintPreview instance by passing WordProcessor.this as the Printable reference. The printData() method obtains a PrinterJob instance, invokes a native Print dialog, and initializes printing the same way as we've seen in previous examples.

The print() method is called to print a given page of the current styled document. First, this method determines the size and origin of the printable area using a PageFormat instance as we've seen before. Next we need to set a clip area of the graphics context to the size of this printable area. This is necessary for the rendering of text component Views because they do clipping area intersection detection for optimized painting. If we don't set the clipping area, they won't know how to render themselves.

Unfortunately, the Printable interface does not provide any methods which can be called to initialize specific resources before printing, and release these resources after printing. So we must implement this functionality ourselves. The actual job of rendering the styled document is done by the m_printView object, which must be instantiated before printing begins, and released when it ends. Being forced to do all this in a single method, we first check if the m_printView reference is null. If it is then we assign it a new instance of PrintVew. If it isn't null we don't modify it (this indicates that we are in the midst of a printing session). When printing ends, we then set it to null so that the remaining PrintView instance can be garbage collected.

    // Only do this once per print

    if (m_printView == null) {

      BasicTextUI btui = (BasicTextUI)m_monitor.getUI();

      View root = btui.getRootView(m_monitor);

      m_printView = new PrintView(

        m_doc.getDefaultRootElement(),

        root, wPage, maxHeight);

    }

To create an m_printView object we need to access the BasicTextUI instance for our m_monitor JTextPane component, and retrieve its root View (which sits on the top of the hierarchy of views--see chapter 19) using BasicTextUI's getRootView() method. At this point the PrintView instance can be created. Its constructor takes four parameters: the root element of the current document, the root view, and the width and height of the entire document's printing bounds.

As soon as we're sure that the m_printView object exists, we call its custom paintPage() method to render a page with the given index to the given graphical context. Then the garbage collector is called explicitly in an attempt to cut down on the heavy memory usage.

Finally if the paintPage() call returns true, the PAGE_EXISTS value is returned to indicate a successful render. Otherwise we set the m_printView reference to null, and return NO_SUCH_PAGE to indicate that no more pages can be rendered.

Class WordProcessor.PrintView

This inner class extends BoxView and is used to render the content of a styled document. (Note that since this class extends BoxView, we have access to some of its protected methods, such as getOffset(), getSpan(), layout(), and paintChild().)

Three instance variables are defined:

int m_firstOnPage: index of the first view to be rendered on the current page.

int m_lastOnPage: index of the last view to be rendered on the current page.

int m_pageIndex: index of the current page.

The PrintView constructor creates the underlying BoxView object for a given root Element instance (this should be the root element in the document model of the text component we are printing) and the specified axis used for format/break operations (this is normally Y_AXIS). A given View instance is then set as the parent for this PrintView (this should be the root View of the text component we are printing). The setSize() method is called to set the size of this view, and layout() is called to lay out the child views based on the specified width and height (this is done to calculate the coordinates of all views used in the rendering of this document). These operations may be time consuming for large documents. Fortunately they are only performed at construction time:

    public PrintView(Element elem, View root, int w, int h) {

      super(elem, Y_AXIS);

      setParent(root);

      setSize(w, h);

      layout(w, h);

    }

Note: We found that setParent()must be called prior to setSize() and layout() to avoid undesirable side effects.

Our paintPage() method renders a single page of a styled document. It takes three parameters:

Graphics g: the graphical context to render the page in.

int hPage: the height of the page.

int pageIndex: the index of the page to render.

This method will return true if the page with the given index is rendered successfully, or false if the end of the document is reached. We assume that the pages to be rendered will be fetched in sequential order (although more than one call can be made to print the most recently rendered page). If a new page index is greater than m_pageIndex (which holds the index of the last rendered page), we begin rendering from the next view after the last one rendered on the previous page, and set m_firstOnPage to m_lastOnPage + 1. If this exceeds the number of child views, no more rendering can be done, so we return false.

      m_firstOnPage = m_lastOnPage + 1;

      if (m_firstOnPage >= getViewCount())

        return false;

Local variables yMin and yMax denote top and bottom coordinates of the page being rendered relative to the top of the document. yMin is determined by the offset of the first view to be rendered, and yMax is then yMin plus the height of the page:

      int yMin = getOffset(Y_AXIS, m_firstOnPage);

      int yMax = yMin + hPage;

All child views, from m_firstOnPage to the last view that will fit on the current page, are examined sequentially in a loop. In each iteration, local variable, Rectangle rc, is assigned the coordinates of where the associated child view is placed in the document (not on the current page). Based on the height of this view, if there is enough room horizontally to render it (note that it is guaranteed to fit vertically, since the page's width was specified in the layout() call above), the paintChild() method is called to render it into the graphics context. Also note that we offset the y-coordinate of the view by yMin because, as we just mentioned, each child view is positioned in terms of the whole document, and we are only concerned with its position on the current page. If at any point a view will not fit within the remaining page space we exit the loop.

      for (int k = m_firstOnPage; k < getViewCount(); k++) {

        rc.x = getOffset(X_AXIS, k);

        rc.y = getOffset(Y_AXIS, k);

        rc.width = getSpan(X_AXIS, k);

        rc.height = getSpan(Y_AXIS, k);

        if (rc.y+rc.height > yMax)

          break;

        m_lastOnPage = k;

          rc.y -= yMin;

        paintChild(g, rc, k);

      }

      return true;

Note: A more sophisticated and precise implementation might examine the y coordinates of all views in the hierarchy, not only the children of the root view. It might be the case that a large paragraph should be split between two or more pages. Our simple approach is not this flexible. In fact, in the case of a paragraph that spans a height larger than the page size, we could be in real trouble with this implementation. Although this is not common, it must be accounted for in professional implementations.

Running the Code

At this point you can compile and execute this example. Figure 22.3 shows a preview of a text document which will occupy four pages. Try previewing and printing a styled document. We've included the  License.rtf file for you to experiment with.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy