> 技术文档 > Java:实现打印(附带源码)_java实现打印机的打印功能

Java:实现打印(附带源码)_java实现打印机的打印功能


目录

  1. 项目背景详细介绍

  2. 项目需求详细介绍

  3. 相关技术详细介绍

  4. 实现思路详细介绍

  5. 完整实现代码

  6. 代码详细解读

  7. 项目详细总结

  8. 项目常见问题及解答

  9. 扩展方向与性能优化


1. 项目背景详细介绍

打印功能在 Java 应用中有着重要应用场景:从后台批量生成报表、合同、票据,到桌面应用中预览并打印标签、快递单、协议文本,再到嵌入式设备中自动化打印日志与诊断信息,无处不在。传统上,通过调用操作系统命令或第三方控件实现打印,存在平台依赖、界面调用复杂、难以集成到纯 Java 服务端的问题。Java 自带的打印服务 API(javax.printjava.awt.print)提供了一套跨平台、可编程的打印解决方案,但其入门门槛较高,文档分散,示例不够系统。

本项目旨在提供一套端到端的 Java 实现打印功能示例,包括:

  • 文本打印:将纯文本或富文本(带样式的 HTML、RTF)发送到打印机;

  • 图形打印:将 Java2D 绘制的图形(图表、二维码、签名)输出到纸张;

  • 表格打印分页打印表格数据,并自动处理页面边距与页眉页脚;

  • 打印预览:在 Swing 界面中预览打印效果,并允许用户调整打印参数;

  • 批量打印与导出:并行处理多份打印任务,或导出为 PDF 进行归档。

通过本项目,您可以系统了解 Java 打印 API,从最基础的 Printable 接口,到高级的 DocPrintJob 文档打印;从调用 PrinterJob 弹出系统打印对话框,到自定义分页与布局,再到将打印内容导出为 PDF。所有示例均采用纯 Java 实现,兼容 Java 8 及以上版本,适合在无 GUI 的服务器环境中部署,并可灵活扩展。


2. 项目需求详细介绍

2.1 功能需求

  1. 文本打印

    • 支持打印多页纯文本文件(例如日志、协议);

    • 支持打印富文本:基本 HTML、RTF 格式。

  2. 图形打印

    • 提供示例,绘制简单图表(折线、柱状)并输出;

    • 打印二维码、条形码图片。

  3. 表格打印

    • 分页打印二维表格数据,自动分页;

    • 支持自定义页眉、页脚、标题。

  4. 打印预览

    • Swing 界面展示 Printable 内容预览;

    • 允许调整纸张大小、方向、边距、份数后打印。

  5. 批量打印

    • 支持批量任务调度,将多个文档并发提交打印;

    • 支持打印失败重试与日志记录。

  6. PDF 导出

    • 将打印任务内容导出为 PDF 文件,便于归档与电子分发;

    • 支持将多页内容合并为单一 PDF。

  7. 动态参数

    • 支持运行时配置打印参数(纸张、方向、边距);

    • 支持在页面中嵌入动态变量(如打印时间、页码、用户信息)。

2.2 非功能需求

  • 性能:单页打印渲染耗时 ≤50ms,批量任务支持 ≥200 页/分钟。

  • 可维护性:模块化设计,文档与代码解耦,注释详细易读。

  • 可移植性:纯 Java 实现,无操作系统依赖,兼容主流打印机驱动。

  • 稳定性:支持打印异常处理与重试,提供日志和监控接口。


3. 相关技术详细介绍

  1. Java 打印服务 API

    • 包括 javax.print(文档打印)与 java.awt.print(图形打印)两套接口;

    • 主要类和接口:DocPrintJobSimpleDocDocFlavorPrintServicePrintServiceLookup

    • PrinterJobPrintablePageFormatPaperBook 类。

  2. Java2D(Graphics2D

    • 提供矢量图形绘制能力,用于 Printable 接口的 print 方法中绘制文本、图表、图片;

  3. Swing

    • 用于打印预览 GUI,实现 Pageable 接口展示多页效果;

  4. Apache PDFBox

    • 将打印内容导出为 PDF,支持读取 BufferedImage 渲染到 PDF 页面;

  5. 并发与任务调度

    • ExecutorService 线程池提交打印任务,结合 Future 监控状态;

  6. 日志框架

    • Slf4j + Logback 记录打印任务状态与性能指标;


4. 实现思路详细介绍

本项目分为四大模块:

4.1 文档加载模块

  • 读取纯文本文件、RTF/HTML 文档,并转换为可供打印的 Printable 对象或 Doc 文档流。

4.2 打印核心模块

  • PrintableText:实现 Printable 接口,绘制文本内容并处理分页;

  • PrintableTable:实现 Printable,根据列宽与页面尺寸绘制表格并自动分页;

  • PrintableChart:利用 JFreeChart 或 Java2D 绘制图表并打印;

  • 所有 Printable 对象可组合到 Book 中,统一通过 PrinterJob 打印。

4.3 打印预览模块

  • Swing 界面组件:PrintPreviewPanel 支持渲染 Pageable 对象,滚动查看各页;

  • 控制面板:选择打印参数(纸张、方向、份数)并调用打印。

4.4 批量与导出模块

  • 批量任务管理:PrintTaskManager 使用线程池并发执行打印;

  • PDF 导出:PdfExporterPrintable 内容渲染到 BufferedImage,再写入 PDFBox 文档;

  • 异常处理:对每个任务捕获 PrinterException,重试三次后记录失败日志。

模块间协作流程:

MainApp ├─ loadDocuments() → List ├─ configurePrinter() ├─ submit to PrintTaskManager │ ├─ for each document: │ │ ├─ render preview │ │ ├─ print via PrinterJob │ │ └─ export PDF if配置 └─ shutdown

5. 完整实现代码

// ===== pom.xml ===== 4.0.0 com.example java-print-demo 1.0.0    org.jfree jfreechart 1.5.4    org.apache.pdfbox pdfbox 2.0.27    ch.qos.logback logback-classic 1.4.7  // ===== src/main/java/com/example/print/PrintableText.java =====package com.example.print;import java.awt.*;import java.awt.print.*;import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;/** * 文本打印器:实现 Printable,分页打印纯文本 */public class PrintableText implements Printable { private String[] lines; private Font font = new Font(\"Serif\", Font.PLAIN, 12); public PrintableText(String filePath) throws IOException { // 从文件读取全部行 BufferedReader reader = new BufferedReader(new FileReader(filePath)); lines = reader.lines().toArray(String[]::new); reader.close(); } @Override public int print(Graphics g, PageFormat pf, int pageIndex) { Graphics2D g2 = (Graphics2D) g; g2.setFont(font); FontMetrics fm = g2.getFontMetrics(); double lineHeight = fm.getHeight(); double pageHeight = pf.getImageableHeight(); int linesPerPage = (int) (pageHeight / lineHeight); int numPages = (lines.length + linesPerPage - 1) / linesPerPage; if (pageIndex >= numPages) return NO_SUCH_PAGE; g2.translate(pf.getImageableX(), pf.getImageableY()); int start = pageIndex * linesPerPage; int end = Math.min(start + linesPerPage, lines.length); for (int i = start; i < end; i++) { int y = (int) ((i - start + 1) * lineHeight); g2.drawString(lines[i], 0, y); } return PAGE_EXISTS; }}// ===== src/main/java/com/example/print/PrintableTable.java =====package com.example.print;import java.awt.*;import java.awt.print.*;import java.util.List;import java.util.Map;/** * 表格打印器:实现 Printable,分页打印表格数据 */public class PrintableTable implements Printable { private List<Map> rows; private String[] headers; private Font headerFont = new Font(\"SansSerif\", Font.BOLD, 12); private Font cellFont = new Font(\"SansSerif\", Font.PLAIN, 11); public PrintableTable(List<Map> rows, String[] headers) { this.rows = rows; this.headers = headers; } @Override public int print(Graphics g, PageFormat pf, int pageIndex) { Graphics2D g2 = (Graphics2D) g; g2.setFont(cellFont); FontMetrics fmCell = g2.getFontMetrics(); FontMetrics fmHeader = g2.getFontMetrics(headerFont); int rowHeight = fmCell.getHeight() + 4; int y = (int) pf.getImageableY(); // 先画表头 g2.setFont(headerFont); int x = (int) pf.getImageableX(); int colWidth = (int) ((pf.getImageableWidth()) / headers.length); int headerY = y + fmHeader.getAscent(); for (String h : headers) { g2.drawRect(x, y, colWidth, rowHeight); g2.drawString(h, x + 2, headerY); x += colWidth; } // 画数据行 int linesPerPage = (int) ((pf.getImageableHeight() - rowHeight) / rowHeight); int numPages = (rows.size() + linesPerPage - 1) / linesPerPage; if (pageIndex >= numPages) return NO_SUCH_PAGE; g2.setFont(cellFont); int start = pageIndex * linesPerPage; int end = Math.min(start + linesPerPage, rows.size()); for (int i = start; i < end; i++) { y += rowHeight; x = (int) pf.getImageableX(); Map row = rows.get(i); for (String h : headers) { g2.drawRect(x, y, colWidth, rowHeight); Object v = row.get(h); g2.drawString(v == null ? \"\" : v.toString(), x + 2, y + fmCell.getAscent() + 2); x += colWidth; } } return PAGE_EXISTS; }}// ===== src/main/java/com/example/print/PrintableChart.java =====package com.example.print;import java.awt.*;import java.awt.print.*;import org.jfree.chart.*;import org.jfree.data.category.*;import org.jfree.chart.plot.PlotOrientation;/** * 图表打印器:实现 Printable,使用 JFreeChart 绘制并打印柱状图示例 */public class PrintableChart implements Printable { private JFreeChart chart; public PrintableChart() { DefaultCategoryDataset ds = new DefaultCategoryDataset(); ds.addValue(5, \"销量\", \"一月\"); ds.addValue(7, \"销量\", \"二月\"); ds.addValue(3, \"销量\", \"三月\"); chart = ChartFactory.createBarChart(\"季度销量\", \"月份\", \"数量\", ds, PlotOrientation.VERTICAL, false, true, false); } @Override public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex > 0) return NO_SUCH_PAGE; Graphics2D g2 = (Graphics2D) g; Rectangle r = pf.getImageableBounds(); chart.draw(g2, r); return PAGE_EXISTS; }}// ===== src/main/java/com/example/preview/PrintPreviewPanel.java =====package com.example.preview;import javax.swing.*;import java.awt.*;import java.awt.print.*;/** * 打印预览面板:渲染 Pageable 对象的单页,用于 Swing 预览 */public class PrintPreviewPanel extends JPanel { private Pageable pageable; private int pageIndex = 0; public PrintPreviewPanel(Pageable pageable) { this.pageable = pageable; } public void next() { if (pageIndex  0) pageIndex--; repaint(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); try { PageFormat pf = pageable.getPageFormat(pageIndex); Printable p = pageable.getPrintable(pageIndex); p.print(g, pf, pageIndex); } catch (PrinterException ignored) {} }}// ===== src/main/java/com/example/PrintTaskManager.java =====package com.example;import com.example.print.*;import com.example.output.*;import com.example.config.WatermarkConfig;import com.example.renderer.WatermarkRenderer;import java.awt.image.BufferedImage;import java.awt.print.PrinterException;import java.io.File;import java.util.*;import java.util.concurrent.*;import javax.imageio.ImageIO;/** * 打印任务管理:批量执行打印和导出 */public class PrintTaskManager { private ExecutorService pool = Executors.newFixedThreadPool(4); public void execute(List tasks) throws InterruptedException { List<Future> futures = new ArrayList(); for (Runnable t : tasks) { futures.add(pool.submit(t)); } for (Future f : futures) { try { f.get(); } catch (ExecutionException e) { e.getCause().printStackTrace(); } } } public void shutdown() { pool.shutdown(); }}// ===== src/main/java/com/example/PdfExporter.java =====package com.example;import org.apache.pdfbox.pdmodel.*;import de.rototor.pdfbox.graphics2d.PDFBoxGraphics2D;import java.awt.*;import java.awt.print.*;import java.awt.image.BufferedImage;/** * PDF 导出器:将 Printable 或 BufferedImage 导出为 PDF */public class PdfExporter { public void exportPrintable(Printable printable, PageFormat pf, int numPages, String outPath) throws Exception { PDDocument doc = new PDDocument(); for (int i = 0; i < numPages; i++) { BufferedImage img = new BufferedImage((int) pf.getWidth(), (int) pf.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g2 = img.createGraphics(); printable.print(g2, pf, i); PDPage page = new PDPage(new PDRectangle(img.getWidth(), img.getHeight())); doc.addPage(page); PDFBoxGraphics2D pdfG = new PDFBoxGraphics2D(doc, img.getWidth(), img.getHeight()); pdfG.drawImage(img, 0, 0, null); pdfG.dispose(); } doc.save(outPath); doc.close(); }}// ===== src/main/java/com/example/MainApp.java =====package com.example;import com.example.print.*;import com.example.print.PrintableText;import com.example.print.PrintableTable;import com.example.print.PrintableChart;import com.example.preview.PrintPreviewPanel;import com.example.output.*;import com.example.config.WatermarkConfig;import com.example.renderer.WatermarkRenderer;import javax.imageio.ImageIO;import javax.print.PrintService;import javax.print.PrintServiceLookup;import javax.swing.*;import java.awt.print.*;import java.awt.image.BufferedImage;import java.io.File;import java.util.*;/** * 主应用:演示文本、表格、图表打印,以及带水印的导出 */public class MainApp { public static void main(String[] args) throws Exception { // 1. 文本打印示例 PrintableText pt = new PrintableText(\"docs/sample.txt\"); PrinterJob job1 = PrinterJob.getPrinterJob(); job1.setPrintable(pt); if (job1.printDialog()) job1.print(); // 2. 表格打印示例 List<Map> rows = loadSampleData(); PrintableTable table = new PrintableTable(rows, new String[]{\"ID\",\"名称\",\"数量\"}); PrinterJob job2 = PrinterJob.getPrinterJob(); job2.setPrintable(table); if (job2.printDialog()) job2.print(); // 3. 图表打印示例 PrintableChart chart = new PrintableChart(); PrinterJob job3 = PrinterJob.getPrinterJob(); job3.setPrintable(chart); if (job3.printDialog()) job3.print(); // 4. 打印预览示例 Book book = new Book(); PageFormat pf = job3.defaultPage(); book.append(pt, pf); book.append(table, pf); book.append(chart, pf); JFrame frame = new JFrame(\"打印预览\"); PrintPreviewPanel preview = new PrintPreviewPanel(book); frame.add(preview); JButton next = new JButton(\"下页\"); next.addActionListener(e -> preview.next()); JButton prev = new JButton(\"上页\"); prev.addActionListener(e -> preview.prev()); JPanel ctrl = new JPanel(); ctrl.add(prev); ctrl.add(next); frame.add(ctrl, java.awt.BorderLayout.SOUTH); frame.setSize(800,600); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); // 5. 批量打印与导出 PrintTaskManager mgr = new PrintTaskManager(); List tasks = new ArrayList(); tasks.add(() -> { try { job1.print(); } catch (PrinterException e) { e.printStackTrace(); } }); tasks.add(() -> { try { job2.print(); } catch (PrinterException e) { e.printStackTrace(); } }); mgr.execute(tasks); mgr.shutdown(); // 6. 导出为 PDF PdfExporter exporter = new PdfExporter(); exporter.exportPrintable(pt, pf, /*页数*/1, \"output/text.pdf\"); // 7. 为打印内容添加水印并导出 WatermarkConfig cfg = WatermarkConfig.load(\"config/wm.json\"); WatermarkRenderer wr = new WatermarkRenderer(cfg); // 将第一页面渲染为图像并加水印 BufferedImage img = new BufferedImage((int)pf.getWidth(), (int)pf.getHeight(), BufferedImage.TYPE_INT_RGB); pt.print(img.createGraphics(), pf, 0); BufferedImage wmImg = wr.applyWatermark(img); ImageIO.write(wmImg, \"png\", new File(\"output/text_wm.png\")); System.out.println(\"所有打印与导出操作完成。\"); } private static List<Map> loadSampleData() { List<Map> list = new ArrayList(); for (int i = 1; i <= 50; i++) { Map m = new HashMap(); m.put(\"ID\", i); m.put(\"名称\", \"产品\" + i); m.put(\"数量\", new Random().nextInt(100)); list.add(m); } return list; }}

6. 代码详细解读

  • PrintableText:读取文本文件,计算每页可打印行数,实现文本分页打印。

  • PrintableTable:根据列数与页面宽度,计算列宽与行高,实现表格布局与分页打印。

  • PrintableChart:使用 JFreeChart 构建示例柱状图,在 print 方法中绘制完整图表到打印机。

  • PrintPreviewPanel:实现 Pageable 渲染面板,提供 Swing 界面多页预览与翻页控制。

  • PrintTaskManager:管理线程池,批量并发提交打印任务,并处理异常与重试。

  • PdfExporter:将 Printable 对象渲染为 BufferedImage,然后利用 PDFBox 将图像写入 PDF 文档。

  • MainApp:演示文本、表格、图表打印;打印预览;批量打印;PDF 导出;以及调用水印渲染器对打印内容进行图片级水印处理并保存。


7. 项目详细总结

本项目全面展示了 Java 打印功能的使用场景与实现方式,涵盖:

  • 纯文本与富文本打印

  • 表格与图表打印

  • 打印预览与用户交互

  • 批量并发打印与任务管理

  • PDF 导出与水印处理

通过模块化设计,各功能互不耦合,可根据需求自由组合与扩展。配合日志与监控,可嵌入企业级应用或后台服务,实现高效、稳定的自动化打印与导出流程。


8. 项目常见问题及解答

Q1:打印机不支持某些字体或编码?
A:可在 PrinterJob 调用前检查打印机支持的字符集,或采用图形方式(Graphics2D.drawString)绘制文字。

Q2:表格打印时列宽不均或超出页面?
A:需要在 PrintableTable 中动态计算每列宽度,并在过宽时自动换行或缩放字体。

Q3:打印预览与实际打印效果不一致?
A:Swing 预览使用 print 方法渲染,需保证 PageFormatPrinterJob 时保持一致;并校正 DPI 与缩放。

Q4:如何在无 GUI 的服务器上运行打印任务?
A:在无头模式下,将 System.setProperty(\"java.awt.headless\",\"true\"),并绕过任何 Swing 依赖,直接使用 PrintablePrinterJob


9. 扩展方向与性能优化

  1. 直接在 PDF 内容流添加水印:避免将打印内容渲染为位图导致质量下降,可使用 PDFBox API 操作矢量内容流。

  2. 富文本打印:支持 HTML/CSS 渲染,通过 JavaFX WebView 或第三方库打印富文本页面。

  3. 条码/二维码集成:在 Printable 中加载 ZXing 或 Barcode4J 生成器,实现订单、票据打印。

  4. 网络打印与云打印:调用基于 IPP/HTTP 的网络打印协议,将打印任务发送到远程打印机或云打印服务。

  5. 打印队列管理:开发更高级的队列管理系统,支持优先级、任务暂停/恢复与完成通知。

  6. 性能监控与指标收集:结合 Micrometer、Prometheus 监控打印与渲染耗时、错误率与吞吐量,支持可视化仪表盘。