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: 5/5 



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

22.5  Printing tables

In this section we'll add printing capabilities to the JTable application developed earlier in chapter 18. Unlike other examples in this chapter, a printed table should not resemble the JTable component as displayed on the screen. This requires us to add detailed code for the rendering of the table's contents as it should be displayed in a printout. The resulting code, however, does not depend on the table's structure and can be easily used for printing any table component. Thus, the code presented here can be plugged into any JTable application that needs printing functionality. Combined with our print preview component (see previous examples), the amount of work we need to do to support printing of tables in professional applicatons is minimal.

Figure 22.6 Print preview of JTable data.

<<figure22-6.gif>>

The Code: StocksTable.java

see \Chapter22\4

import java.awt.*;

import java.awt.event.*;

import java.util.*;

import java.io.*;

import java.text.*;

import java.sql.*;

import java.awt.print.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.event.*;

import javax.swing.table.*;

public class StocksTable extends JFrame implements Printable

{

  protected JTable m_table;

  protected StockTableData m_data;

  protected JLabel m_title;

  protected int m_maxNumPage = 1;

  // Unchanged code from section 18.6

  protected JMenuBar createMenuBar() {

    // Unchanged code from section 18.6

    JMenuItem mPrint = new JMenuItem("Print...");

    mPrint.setMnemonic('p');

    ActionListener lstPrint = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        Thread runner = new Thread() {

          public void run() {

            printData();

          }

        };

        runner.start();

      }

    };

    mPrint.addActionListener(lstPrint);

    mFile.add(mPrint);

    JMenuItem mPreview = new JMenuItem("Print Preview");

    mPreview.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(Table7.this,

            m_title.getText()+" preview");

            setCursor(Cursor.getPredefinedCursor(

              Cursor.DEFAULT_CURSOR));

          }

        };

        runner.start();

      }

    };

    mPreview.addActionListener(lstPreview);

    mFile.add(mPreview);

    mFile.addSeparator();

    // Unchanged code from section 18.6

  }

  public void printData() {

    try {

      PrinterJob prnJob = PrinterJob.getPrinterJob();

      prnJob.setPrintable(this);

      if (!prnJob.printDialog())

        return;

      m_maxNumPage = 1;

      prnJob.print();

    }

    catch (PrinterException e) {

      e.printStackTrace();

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

    }

  }

  public int print(Graphics pg, PageFormat pageFormat,

   int pageIndex) throws PrinterException {

    if (pageIndex >= m_maxNumPage)

      return NO_SUCH_PAGE;

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

      (int)pageFormat.getImageableY());

    int wPage = 0;

    int hPage = 0;

    if (pageFormat.getOrientation() == pageFormat.PORTRAIT) {

      wPage = (int)pageFormat.getImageableWidth();

      hPage = (int)pageFormat.getImageableHeight();

    }

    else {

      wPage = (int)pageFormat.getImageableWidth();

      wPage += wPage/2;

      hPage = (int)pageFormat.getImageableHeight();

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

    }

    int y = 0;

    pg.setFont(m_title.getFont());

    pg.setColor(Color.black);

    Font fn = pg.getFont();

    FontMetrics fm = pg.getFontMetrics();

    y += fm.getAscent();

    pg.drawString(m_title.getText(), 0, y);

    y += 20; // space between title and table headers

    Font headerFont = m_table.getFont().deriveFont(Font.BOLD);

    pg.setFont(headerFont);

    fm = pg.getFontMetrics();

    TableColumnModel colModel = m_table.getColumnModel();

    int nColumns = colModel.getColumnCount();

    int x[] = new int[nColumns];

    x[0] = 0;

    int h = fm.getAscent();

    y += h; // add ascent of header font because of baseline

            // positioning (see figure 2.10)

    int nRow, nCol;

    for (nCol=0; nCol<nColumns; nCol++) {

      TableColumn tk = colModel.getColumn(nCol);

      int width = tk.getWidth();

      if (x[nCol] + width > wPage) {

        nColumns = nCol;

        break;

      }

      if (nCol+1<nColumns)

        x[nCol+1] = x[nCol] + width;

      String title = (String)tk.getIdentifier();

      pg.drawString(title, x[nCol], y);

    }

    pg.setFont(m_table.getFont());

    fm = pg.getFontMetrics();

    int header = y;

    h = fm.getHeight();

    int rowH = Math.max((int)(h*1.5), 10);

    int rowPerPage = (hPage-header)/rowH;

    m_maxNumPage = Math.max((int)Math.ceil(m_table.getRowCount()/

      (double)rowPerPage), 1);

    TableModel tblModel = m_table.getModel();

    int iniRow = pageIndex*rowPerPage;

    int endRow = Math.min(m_table.getRowCount(),

      iniRow+rowPerPage);

    for (nRow=iniRow; nRow<endRow; nRow++) {

      y += h;

      for (nCol=0; nCol<nColumns; nCol++) {

        int col = m_table.getColumnModel().getColumn(nCol).getModelIndex();

        Object obj = m_data.getValueAt(nRow, col);

        String str = obj.toString();

        if (obj instanceof ColorData)

          pg.setColor(((ColorData)obj).m_color);

        else

          pg.setColor(Color.black);

          pg.drawString(str, x[nCol], y);

      }

    }

    System.gc();

    return PAGE_EXISTS;

  }

// Remaining code unchanged from section 18.6

Understanding the Code

Class StocksTable

In comparison with the table examples of chapter 18, we now implement the Printable interface. In our createMenuBar() method we add a "Print..." menu item, which calls our new printData() method which acts just like the printData() methods we implemented in the examples above.

In our implementation of the print() method, we first determine whether a valid page index has been specified by comparing it to the maximum number of pages, m_maxNumPage:

        if (pageIndex > m_maxNumPage)

            return NO_SUCH_PAGE;

The catch is that we don't know this maximum number in advance. So we assign an initial value of 1 to m_maxNumPage (the code above works for the 0-th page), and adjust m_maxNumPage to the real value later in the code, just as we've done in the examples above.

We then translate the origin of the graphics context to the origin of the given PageFormat instance and determine the width and height of the area available for printing. These dimensions are used to determine how much data can fit on the given page. This same technique was also used in the previous examples. However, in this example we've added the ability to print with a landscape orientation because tables can be quite wide, and we normally don't want table data to span multiple pages (at least horizontally). In order to do this we have to first check the orientation of the given PageFormat instance. If it is PORTRAIT we determine its width and height as we have always done. If it is not PORTRAIT, then it must be either LANDSCAPE or REVERSE_LANDSCAPE (see section 22.1.5). In this case we need to increase the width of the page because the default is not adequate. After increasing the width we must also explicitly set the size of the graphics clip,

This is all we have to do to allow printing in either orientation.

Local variable y is created to keep track of the current vertical position on the page, and we are now ready to actually start the rendering, and begin with the the table's title. Note that we use the same font as is used in the table application for consistency. We add some white space below the title (by increasing y) and then we make preparations for printing our table's headers and body. A bold font is used for our table's header. An array, x[], is created which will be used to store the x coordinate of each column's upper left corner (taking into account that they may be resized and moved). Variable nColumns contains the total number of columns in our table.

Now we actually iterate through the columns and print each column header while filling our x[] array. We check each iteration to see if the x coordinate of the previous column, combined with the width of the column under consideration, will be more than the width of the page. If so we set the total number of columns, nColumns, to the number that will actually fit on the page, and then break out of the loop. If not we set the x coordinate corresponding to the current column, print its title, and continue on to the next iteration.

Since we've completed the printing of our table's title and headers, we know how much space is left for printing our table's body. We also know the font's height, so we can calculate how many rows can be printed on one page, which is rowPerPage below (the height of the page minus the current y offset, all divided by the height of the current font or 10, whichever is larger). Finally we calculate the real number of pages, m_maxNumPage, by dividing the total row count of our table by the number of rows per page we just calculated as rowPerPage. The minimum page count will be 1.

Now we need to actually print the table data. First we calculate the initial iniRow and final endRow rows to be printed on this page:

        TableModel tblModel = m_table.getModel();

        int iniRow = pageIndex*rowPerPage;

        int endRow = Math.min(m_table.getRowCount(),

            iniRow+rowPerPage);

Then, in a double for loop, iterating through each column of each row in turn, we print the table's contents. This is done by extracting each cell's data as an Object (using getValueAt()). We store its toString() String representation in a local variable and check if the object is an instance of our custom inner class, ColorData (defined in earlier chapter 18 examples). This class is designed to associate a color with a given data object. So if the object is a ColorData instance we grab its color and assign it as the current color of the graphics context. If it isn't we use black. Finally, we print that object's toString() representation and continue on to the remaining cells.

Note: We are assuming that each object's toString() representation is what we want to print. For more complex TableCellRenderer implementations, this printing code will need to be customized.

We end by explicitly invoking the garbage collector and returning PAGE_EXISTS to indicate a successful print.

Running the Code

At this point you can compile and execute this example. Figure 22.4 shows a print preview of our table application. Try manipulating the table's contents (by choosing different dates if you have JDBC and ODBC -- see chapter 18) and column orders to see how it affects the table's printout and print preview.

You will notice that in order to fit the whole table on the paper it must be condensed considerably. It is natural at this point to want to print it with a landscape orientation. When we choose landscape from the Page Setup dialog this modifies the PageFormat object that will be sent to our print() method when printing begins. However, this will not actually tell the printer to print in landscape mode. In order to do this, we have to explicitly choose landscape mode from the Print dialog as well. Unfortunately, the Page Setup information does not inform the printer, but it is necessary to inform our application.

Though our application can print successfully with a landscape orientation, our print preview component is not designed to display anything but portrait-oriented previews. Because of the way our PrintPreview component has been constructed, it is quite easy to add the ability to preview landscape-oriented pages if desired. The only modification that is necessary, is the addition of a parameter to its constructor which specifyies the orientation to use. This parameter can then be assigned to the PageFormat object used in constructing each PagePreview object. We will not show the code here, but we have included a modified version of PrintPreview and the StocksTable application to demonstrate how you can implement this functionality. See \Chapter22\5. Figure 22.7 illustrates.

Figure 22.7 Print preview component modified for landscape orientation.

<<figure22-7.gif>>



Thanks to John Sullivan of WebScope, Inc. for his valuable detective work.



[ 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