Java Advanced Printing

Training Index

[<<BACK] [CONTENTS] [NEXT>>]

The previous section explained how to print simple components and covered techniques that can be used to print screen captures. However, if you want to print more than one component per page, or if your component is larger than one page size, you need to do some additional work inside your print method. This section explains what you need to do and concludes with an example of how to print the contents of a JTable component.

Multiple Components Per Page

There are times when printing one component on a page does not meet your printing needs. For example, you might want to include a header on each page or print a footer with the page number--something that isn't necessarily displayed on the screen.

Unfortunately, printing multiple customized components on a page is not as easy as adding additional paint calls because each paint call overwrites the output of the previous call.

The key to printing more than one component on a page, is to use the translate(double, double) and setClip methods in the Graphics2D class.

The translate method moves an imaginary pen to the next position of the print output where the component can be painted and then printed. There are two translate methods in the Graphics2D class. To print multiple components you need the one t hat takes double arguments because this translate method allows relative positioning. Be sure to cast any integer values to double or float. Relative positioning in this context means that previous calls to translate are taken into account when calculating the new translated point.

The setClip method is used to restrict the component to only be painted, and therefore printed, in the area specified. This lets you print multiple components on a page by moving the imaginary pen to different points on the page and then painting each component in the clip area.

Example

You can replace the print method in the Abstract Window Toolkit (AWT) and Swing printbutton.java examples with the following code to add the footer message Company Confidential to the page.


public int print(Graphics g, PageFormat pf, int pi)
throws PrinterException {

if (pi >= 1) {
return Printable.NO_SUCH_PAGE;
}

Graphics2D g2 = (Graphics2D) g;
Font f= Font.getFont("Courier");
double height=pf.getImageableHeight();
double width=pf.getImageableWidth();

g2.translate(pf.getImageableX(),
pf.getImageableY());
g2.setColor(Color.black);
g2.drawString("Company Confidential", (int)width/2,
(int)height-g2.getFontMetrics().getHeight());
g2.translate(0f,0f);
g2.setClip(0,0,(int)width,
(int)(height-g2.getFontMetrics().getHeight()*2));
paint (g2);
return Printable.PAGE_EXISTS;
}

In the new print method, the Graphics2D context is clipped before calling the parent JButton paint method. This prevents the JButton paint method from overwriting the bottom of the page. The translate method is used to point the JButton paint method to start the paint at offset 0,0 from the visible part of the page. The visible area was already calculated by the previous translate call:

  g2.translate(pf.getImageableX(), pf.getImageableY());

For some components, you might also need to set the foreground color to see your results. In this example the text color was printed in black.

Useful Methods To Call In The print Method

The following methods are useful for calculating the number of pages required and for shrinking components to fit on a page:

PageFormat methods:

getImageableHeight()
returns the page height you can user for printing your output.

getImageableWidth()
returns the page width you can use for printing your output.

Graphics2D method:

scale(xratio, yratio)
scales the 2D graphics context by this size. A ratio of one maintains the size, less than one will shrink the graphics context.

Components Larger Than One Page

The Java 2 Printing API has a Book API that provides the concept of pages. However, the Book API only adds printable objects to a collection of printable objects. It does not calculate page breaks or split components over multiple pages.

When printing a simple component on a page, you only have to check for the index value being greater or equal to one and return NO_SUCH_PAGE when this value is reached.

To print multiple pages, you have to calculate the number of pages needed to contain the component. You can calculate the total number of pages needed by subtracting the space taken by the component from the value returned by getImageableHeight. Once the total number of pages is calculated, you can run the following check inside the print method:

  if (pageIndex >=TotalPages) {
return NO_SUCH_PAGE;
}

The Printing framework calls the print method multiple times until pageIndex is less than or equal to TotalPages. All you need to do is create a new page from the same component on each print loop. This is done by treating the printed page like a sliding window over the component. The part of the component that is to be printed is selected by a translate call to mark the top of the page and a setClip call to mark the bottom of the page. The following diagram illustrates this process.

The left side of the diagram represents the page sent to the printer. The right side contains the long component being printed in the print method.

The first page can be represented as follows:

The printed page window then slides along the component to print the second page, page index one.

This process continues until the last page from the total number of pages is reached:

Printing A JTable Component

The Report.java class uses many of the advanced techniques covered in this section to print out the data and header of a JTable component that can span many pages. The printed output also includes a footer at the bottom with the page number.

This diagram shows how the report looks when it prints:


import javax.swing.*;
import javax.swing.table.*;
import java.awt.print.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.Dimension;

public class Report implements Printable{
JFrame frame;
JTable tableView;

public Report() {
frame = new JFrame("Sales Report");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);}});

final String[] headers = {"Description", "open price",
"latest price", "End Date", "Quantity"};
final Object[][] data = {
{"Box of Biros", "1.00", "4.99", new Date(),
new Integer(2)},
{"Blue Biro", "0.10", "0.14", new Date(),
new Integer(1)},
{"legal pad", "1.00", "2.49", new Date(),
new Integer(1)},
{"tape", "1.00", "1.49", new Date(),
new Integer(1)},
{"stapler", "4.00", "4.49", new Date(),
new Integer(1)},
{"legal pad", "1.00", "2.29", new Date(),
new Integer(5)}
};

TableModel dataModel = new AbstractTableModel() {
public int getColumnCount() {
return headers.length; }
public int getRowCount() { return data.length;}
public Object getValueAt(int row, int col) {
return data[row][col];}
public String getColumnName(int column) {
return headers[column];}
public Class getColumnClass(int col) {
return getValueAt(0,col).getClass();}
public boolean isCellEditable(int row, int col) {
return (col==1);}
public void setValueAt(Object aValue, int row,
int column) {
data[row][column] = aValue;
}
};

tableView = new JTable(dataModel);
JScrollPane scrollpane = new JScrollPane(tableView);

scrollpane.setPreferredSize(new Dimension(500, 80));
frame.getContentPane().setLayout(
new BorderLayout());
frame.getContentPane().add(
BorderLayout.CENTER,scrollpane);
frame.pack();
JButton printButton= new JButton();

printButton.setText("print me!");

frame.getContentPane().add(
BorderLayout.SOUTH,printButton);

// for faster printing turn double buffering off

RepaintManager.currentManager(
frame).setDoubleBufferingEnabled(false);

printButton.addActionListener( new ActionListener(){
public void actionPerformed(ActionEvent evt) {
PrinterJob pj=PrinterJob.getPrinterJob();
pj.setPrintable(Report.this);
pj.printDialog();
try{
pj.print();
}catch (Exception PrintException) {}
}
});

frame.setVisible(true);
}

public int print(Graphics g, PageFormat pageFormat,
int pageIndex) throws PrinterException {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.black);
int fontHeight=g2.getFontMetrics().getHeight();
int fontDesent=g2.getFontMetrics().getDescent();

//leave room for page number
double pageHeight =
pageFormat.getImageableHeight()-fontHeight;
double pageWidth =
pageFormat.getImageableWidth();
double tableWidth = (double)
tableView.getColumnModel(
).getTotalColumnWidth();
double scale = 1;
if (tableWidth >= pageWidth) {
scale = pageWidth / tableWidth;
}

double headerHeightOnPage=
tableView.getTableHeader(
).getHeight()*scale;
double tableWidthOnPage=tableWidth*scale;

double oneRowHeight=(tableView.getRowHeight()+
tableView.getRowMargin())*scale;
int numRowsOnAPage=
(int)((pageHeight-headerHeightOnPage)/
oneRowHeight);
double pageHeightForTable=oneRowHeight*
numRowsOnAPage;
int totalNumPages=
(int)Math.ceil((
(double)tableView.getRowCount())/
numRowsOnAPage);
if(pageIndex>=totalNumPages) {
return NO_SUCH_PAGE;
}

g2.translate(pageFormat.getImageableX(),
pageFormat.getImageableY());
//bottom center
g2.drawString("Page: "+(pageIndex+1),
(int)pageWidth/2-35, (int)(pageHeight
+fontHeight-fontDesent));

g2.translate(0f,headerHeightOnPage);
g2.translate(0f,-pageIndex*pageHeightForTable);

//If this piece of the table is smaller
//than the size available,
//clip to the appropriate bounds.
if (pageIndex + 1 == totalNumPages) {
int lastRowPrinted =
numRowsOnAPage * pageIndex;
int numRowsLeft =
tableView.getRowCount()
- lastRowPrinted;
g2.setClip(0,
(int)(pageHeightForTable * pageIndex),
(int) Math.ceil(tableWidthOnPage),
(int) Math.ceil(oneRowHeight *
numRowsLeft));
}
//else clip to the entire area available.
else{
g2.setClip(0,
(int)(pageHeightForTable*pageIndex),
(int) Math.ceil(tableWidthOnPage),
(int) Math.ceil(pageHeightForTable));
}

g2.scale(scale,scale);
tableView.paint(g2);
g2.scale(1/scale,1/scale);
g2.translate(0f,pageIndex*pageHeightForTable);
g2.translate(0f, -headerHeightOnPage);
g2.setClip(0, 0,
(int) Math.ceil(tableWidthOnPage),
(int)Math.ceil(headerHeightOnPage));
g2.scale(scale,scale);
tableView.getTableHeader().paint(g2);
//paint header at top

return Printable.PAGE_EXISTS;
}

public static void main(String[] args) {
new Report();
}
}

Print a Sales Report

The SalesReport.java Applet class prints a sales report with the rows split over multiple pages with numbers at the bottom of each page. Here is how the application looks when launched:

You need this policy file to launch the applet:

grant {
permission java.lang.RuntimePermission
"queuePrintJob";
};

To launch the applet assuming a policy file named printpol and an HTML file named SalesReport.html, you would type:

  appletviewer -J-Djava.security.policy=
printpol SalesReport.html

This diagram shows how the report prints:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值