Java打印功能全解析
立即解锁
发布时间: 2025-08-18 00:08:27 阅读量: 2 订阅数: 9 

# Java 打印功能全解析
## 1. 打印功能的发展历程
早期的 Java 开发工具包完全不支持打印功能,小程序无法进行打印操作,若要在应用程序中实现打印,需借助第三方库。SDK 1.1 引入了轻量级的打印支持,能实现简单的打印输出,但打印质量有限。其打印模型旨在让浏览器厂商打印小程序在网页上的显示内容,不过未得到广泛应用。
SDK 1.2 开启了与 2D 图形深度集成的强大打印模型,SDK 1.3 进行了小幅度改进,而 SDK 1.4 则带来了重要增强,如发现打印机特性和支持服务器端打印管理的流式打印作业。
## 2. 打印 2D 图形
### 2.1 基本要求
要生成打印输出,需完成以下两个关键步骤:
- 提供一个实现 `Printable` 接口的对象。
- 启动打印作业。
`Printable` 接口仅有一个方法:
```java
int print(Graphics g, PageFormat format, int page)
```
此方法在打印引擎需要对页面进行打印格式设置时被调用。在该方法中,需将需要打印的文本和图像绘制到图形上下文中。`PageFormat` 参数提供纸张大小和打印边距信息,`page` 参数指明要渲染的页面编号。
### 2.2 启动打印作业
使用 `PrinterJob` 类启动打印作业,具体步骤如下:
1. 调用静态方法 `getPrinterJob` 获取打印作业对象。
2. 设置要打印的 `Printable` 对象。
示例代码如下:
```java
Printable canvas = ...;
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(canvas);
```
### 2.3 打印对话框
在启动打印作业前,建议调用 `printDialog` 方法显示打印对话框,让用户选择打印机、打印页面范围及其他打印设置。示例代码如下:
```java
HashPrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
if (job.printDialog(attributes)) {
try {
job.print();
} catch (PrinterException exception) {
// 处理异常
}
}
```
### 2.4 页面格式
`PageFormat` 类提供了关于打印页面的详细信息,如纸张大小、可打印区域的尺寸和位置等。相关方法如下:
- `getWidth()` 和 `getHeight()`:返回纸张的宽度和高度,单位为点(1 点等于 1/72 英寸)。
- `getImageableWidth()` 和 `getImageableHeight()`:返回可打印区域的宽度和高度。
- `getImageableX()` 和 `getImageableY()`:返回可打印区域左上角的坐标。
### 2.5 示例代码
以下是一个完整的示例,展示如何在屏幕和打印页面上渲染相同的图形:
```java
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.util.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.swing.*;
public class PrintTest {
public static void main(String[] args) {
JFrame frame = new PrintTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
class PrintTestFrame extends JFrame {
public PrintTestFrame() {
setTitle("PrintTest");
setSize(300, 300);
Container contentPane = getContentPane();
PrintPanel canvas = new PrintPanel();
contentPane.add(canvas, BorderLayout.CENTER);
PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
JPanel buttonPanel = new JPanel();
JButton printButton = new JButton("Print");
buttonPanel.add(printButton);
printButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
try {
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(canvas);
if (job.printDialog(attributes)) {
job.print(attributes);
}
} catch (PrinterException exception) {
JOptionPane.showMessageDialog(PrintTestFrame.this, exception);
}
}
});
JButton pageSetupButton = new JButton("Page setup");
buttonPanel.add(pageSetupButton);
pageSetupButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PrinterJob job = PrinterJob.getPrinterJob();
job.pageDialog(attributes);
}
});
contentPane.add(buttonPanel, BorderLayout.NORTH);
}
}
class PrintPanel extends JPanel implements Printable {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
drawPage(g2);
}
public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
if (page >= 1) return Printable.NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D) g;
g2.translate(pf.getImageableX(), pf.getImageableY());
g2.draw(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf.getImageableHeight()));
drawPage(g2);
return Printable.PAGE_EXISTS;
}
public void drawPage(Graphics2D g2) {
FontRenderContext context = g2.getFontRenderContext();
Font f = new Font("Serif", Font.PLAIN, 72);
GeneralPath clipShape = new GeneralPath();
TextLayout layout = new TextLayout("Hello", f, context);
AffineTransform transform = AffineTransform.getTranslateInstance(0, 72);
Shape outline = layout.getOutline(transform);
clipShape.append(outline, false);
layout = new TextLayout("World", f, context);
transform = AffineTransform.getTranslateInstance(0, 144);
outline = layout.getOutline(transform);
clipShape.append(outline, false);
g2.draw(clipShape);
g2.clip(clipShape);
final int NLINES = 50;
Point2D p = new Point2D.Double(0, 0);
for (int i = 0; i < NLINES; i++) {
double x = (2 * getWidth() * i) / NLINES;
double y = (2 * getHeight() * (NLINES - 1 - i)) / NLINES;
Point2D q = new Point2D.Double(x, y);
g2.draw(new Line2D.Double(p, q));
}
}
}
```
### 2.6 使用本地打印对话框
在 SDK 1.4 之前,打印系统使用主机平台的本地打印和页面设置对话框。显示本地打印对话框,可调用无参数的 `printDialog` 方法;显示本地页面设置对话框,需将默认的 `PageFormat` 对象传递给 `pageDialog` 方法。
## 3. 多页打印
实际应用中,通常不直接将 `Printable` 对象传递给打印作业,而是使用实现 `Pageable` 接口的对象,Java 平台提供了 `Book` 类。`Book` 由多个 `Printable` 部分组成,可通过添加 `Printable` 对象及其页面数量来创建。
示例代码如下:
```java
Book book = new Book();
Printable coverPage = ...;
Printable bodyPages = ...;
book.append(coverPage, pageFormat); // 追加 1 页
book.append(bodyPages, pageFormat, pageCount);
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPageable(book);
```
使用 `Book` 类的挑战在于,打印前需明确每个部分的页面数量,因此 `Printable` 类需要一个布局算法来计算打印页面上内容的布局。同时,要考虑用户更改页面格式的情况,若发生更改,需重新计算布局。
以下是一个多页打印的示例代码:
```java
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.util.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.swing.*;
public class BookTest {
public static void main(String[] args) {
JFrame frame = new BookTestFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
class BookTestFrame extends JFrame {
private JTextField text;
private PageFormat pageFormat;
private PrintRequestAttributeSet attributes;
private static final int WIDTH = 300;
private static final int HEIGHT = 100;
public BookTestFrame() {
setTitle("BookTest");
setSize(WIDTH, HEIGHT);
Container contentPane = getContentPane();
text = new JTextField();
contentPane.add(text, BorderLayout.NORTH);
attributes = new HashPrintRequestAttributeSet();
JPanel buttonPanel = new JPanel();
JButton printButton = new JButton("Print");
buttonPanel.add(printButton);
printButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
try {
PrinterJob job = PrinterJob.getPrinterJob();
job.setPageable(makeBook());
if (job.printDialog(attributes)) {
job.print(attributes);
}
} catch (PrinterException exception) {
JOptionPane.showMessageDialog(BookTestFrame.this, exception);
}
}
});
JButton pageSetupButton = new JButton("Page setup");
buttonPanel.add(pageSetupButton);
pageSetupButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PrinterJob job = PrinterJob.getPrinterJob();
pageFormat = job.pageDialog(attributes);
}
});
JButton printPreviewButton = new JButton("Print preview");
buttonPanel.add(printPreviewButton);
printPreviewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PrintPreviewDialog dialog = new PrintPreviewDialog(makeBook());
dialog.show();
}
});
contentPane.add(buttonPanel, BorderLayout.SOUTH);
}
public Book makeBook() {
if (pageFormat == null) {
PrinterJob job = PrinterJob.getPrinterJob();
pageFormat = job.defaultPage();
}
Book book = new Book();
String message = text.getText();
Banner banner = new Banner(message);
int pageCount = banner.getPageCount((Graphics2D) getGraphics(), pageFormat);
book.append(new CoverPage(message + " (" + pageCount + " pages)"), pageFormat);
book.append(banner, pageFormat, pageCount);
return book;
}
}
class Banner implements Printable {
private String message;
private double scale;
public Banner(String m) {
message = m;
}
public int getPageCount(Graphics2D g2, PageFormat pf) {
if (message.equals("")) return 0;
FontRenderContext context = g2.getFontRenderContext();
Font f = new Font("Serif", Font.PLAIN, 72);
Rectangle2D bounds = f.getStringBounds(message, context);
scale = pf.getImageableHeight() / bounds.getHeight();
double width = scale * bounds.getWidth();
int pages = (int) Math.ceil(width / pf.getImageableWidth());
return pages;
}
public int print(Graphics g, PageFormat pf, int page) throws PrinterException {
Graphics2D g2 = (Graphics2D) g;
if (page > getPageCount(g2, pf)) return Printable.NO_SUCH_PAGE;
g2.translate(pf.getImageableX(), pf.getImageableY());
drawPage(g2, pf, page);
return Printable.PAGE_EXISTS;
}
public void drawPage(Graphics2D g2, PageFormat pf, int page) {
if (message.equals("")) return;
page--; // 考虑封面页
drawCropMarks(g2, pf);
g2.clip(new Rectangle2D.Double(0, 0, pf.getImageableWidth(), pf.getImageableHeight()));
g2.translate(-page * pf.getImageableWidth(), 0);
g2.scale(scale, scale);
FontRenderContext context = g2.getFontRenderContext();
Font f = new Font("Serif", Font.PLAIN, 72);
TextLayout layout = new TextLayout(message, f
```
0
0
复制全文