新接手一个打印终端的项目,要求可以打印A4格式的单据和 70mm * 40mm 规格的条码。

整体流程可分两种情况,

一种是将打印模板转换为pdf文档二进制数组,进而生成为pdf文档,保存到本地,然后再读取到程序中,打印,最后删除生成的pdf文档(不然随着打印次数的增多,本地磁盘岂不爆满);

另一种是省略保存中间步骤,直接将打印模板转换得到的pdf文档二进制数组用于程序打印。

显然,第二种情况较为简单,项目最后也是采用的这种。

先说打印A4的情况。

打印A4的情况比较简单,本地有一台惠普的A4打印,直接上代码。

package com.jiuqi.dna.gams.GXH.yndx.printing.util;import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;import javax.print.PrintService;import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable;
import org.apache.pdfbox.printing.PDFPrintable;
import org.apache.pdfbox.printing.Scaling;import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;import net.sf.json.JSONObject;/*** 自助打印终端打印工具类* @author wangjiao01**/
public class PDFPrintUtil {/*** 获取临时生成的pdf文件路径* @param pdfData* @return*/public static String getNewPDFPath(byte[] pdfData) {DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");String newPdfName = df.format(new Date());String newPdfPath = "E:\\pdf\\" + newPdfName + ".pdf";// 随具体环境变化OutputStream outputStream = null;try {outputStream = new FileOutputStream(newPdfPath);outputStream.write(pdfData);}catch(IOException e) {e.printStackTrace();}finally {if(outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}return newPdfPath;}/*** 执行打印* @param pdfData pdf文档对应的二进制数组* @param printerName 打印机标识* @param copyCount 打印份数* @return* @throws IOException*/public static String doPrintByPDFBox(byte[] pdfData, String printerName, Integer copyCount) throws IOException {String result = null;PDDocument document = null;try {document = PDDocument.load(pdfData);PrinterJob printerJob = PrinterJob.getPrinterJob();// 查找并设置打印机PrintService[] printServices = PrinterJob.lookupPrintServices();if(printServices == null || printServices.length == 0) {result = getPrintMessage(false, "打印失败,计算机未安装打印机,请检查。");
//              makeSound("打印失败,计算机未安装打印机,请检查。");return result;}PrintService printService = null;for(int i = 0; i < printServices.length; i++) {if(printServices[i].getName().equalsIgnoreCase(printerName)) {System.out.println(printServices[i].getName());printService = printServices[i];break;}}if(printService != null) {printerJob.setPrintService(printService);} else {result = getPrintMessage(false, "打印失败,未找到名称为" + printerName + "的打印机,请检查。");
//              makeSound("打印失败,未找到名称为" + printerName + "的打印机,请检查。");return result;}// 设置纸张PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);PageFormat pageFormat = new PageFormat();pageFormat.setOrientation(PageFormat.PORTRAIT);pageFormat.setPaper(getPaper(printerName));// Book 的方式实现打印多张(已测试,可行)Book book = new Book();book.append(pdfPrintable, pageFormat, document.getNumberOfPages());printerJob.setPageable(book);// PDFPageable 的方式实现打印多张(未测试,应该也可行)
//          PDFPageable pdfPageable = new PDFPageable(document);
//          pdfPageable.append(pdfPrintable, pageFormat, document.getNumberOfPages());
//          printerJob.setPageable(pdfPageable);// 测试System.out.println(document.getNumberOfPages());System.out.println(book.getNumberOfPages());
//          System.out.println(pdfPageable.getNumberOfPages());// 执行打印printerJob.setCopies(copyCount);printerJob.print();result = getPrintMessage(true, "打印成功。");
//          makeSound("打印成功,请取件。");} catch (Exception e) {e.printStackTrace();result = getPrintMessage(false, "打印失败:发生异常。");
//          makeSound("打印失败,打印时发生异常,请检查。");} finally {if(document != null) {document.close();// 起初文件删除失败,关闭文档之后,删除成功}}return result;}/*** 获取打印结果信息,成功或失败,用以返回前台界面* @param isPrintSuccess* @param message* @return*/public static String getPrintMessage(boolean isPrintSuccess, String message) {JSONObject object = new JSONObject();if(isPrintSuccess) {object.put("code", 1);}else {object.put("code", 0);}object.put("message", message);System.out.println(message);return object.toString();}/*** 删除打印过程中创建的临时pdf文件* @param newPdfPath* @return*/public static boolean deleteFile(String newPdfPath) {File file = new File(newPdfPath);if(file.exists()) {if(file.isFile()) {return file.delete();}}else {System.out.println("文件 " + newPdfPath + " 不存在!");}return false;}/*** 打印语音提示:成功或失败,并提示失败原因* @param message*/public static void makeSound(String message) {ActiveXComponent sap = new ActiveXComponent("Sapi.SpVoice");try {// 音量 0-100sap.setProperty("Volume", new Variant(100));// 语音朗读速度 -10 到 +10sap.setProperty("Rate", new Variant(0));// 获取执行对象Dispatch sapo = sap.getObject();// 执行朗读Dispatch.call(sapo, "Speak", new Variant(message));// 关闭执行对象sapo.safeRelease();} catch (Exception e) {e.printStackTrace();} finally {// 关闭应用程序连接sap.safeRelease();}}/*** 根据打印机名称判断是单据打印还是条码打印,进而创建对应Paper对象并返回* @param printerName* @return*/public static Paper getPaper(String printerName) {Paper paper = new Paper();// 默认为A4纸张,对应像素宽和高分别为 595, 848int width = 595;int height = 848;// 设置边距,单位是像素,10mm边距,对应 28pxint marginLeft = 10;int marginRight = 0;int marginTop = 10;int marginBottom = 0;if(printerName.contains("bar")) {// 云南大学条码纸张规格70mm宽*40mm高,对应像素值为 198, 113width = 198;height = 113;}paper.setSize(width, height);// 下面一行代码,解决了打印内容为空的问题paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom));return paper;}}

经过探索与研究,对PDFBox的一些类有了一定的认识,现记录如下,以便大家今后使用。

1、PDDocument,对应一个实际的pdf文档,有好多个重载的静态load方法,用于将实际pdf文档创建到内存中。

getNumberOfPages方法可获取文档的总页数。

2、PDFPrintable,对应的API类注释为 Prints pages from a PDF document using any page size or scaling mode.

实际使用举例:

PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);

经常用以作为Book或Pageable类的方法参数。

3、PageFormat,API对应的类注释为 The <code>PageFormat</code> class describes the size and orientation of a page to be printed. 用于格式化打印规格,如设置打印方向(横向和纵向)、打印纸张等。

实际使用举例:

pageFormat.setOrientation(PageFormat.PORTRAIT);// 设置打印方向为纵向。PageFormat.PORTRAIT表示纵向,PageFormat.LANDSCAPE表示横向
pageFormat.setPaper(getPaper(printerName));// 设置纸张规格

具体获取纸张方法代码如下:

/*** 根据打印机名称判断是单据打印还是条码打印,进而创建对应Paper对象并返回* @param printerName* @return*/public static Paper getPaper(String printerName) {Paper paper = new Paper();// 默认为A4纸张,对应像素宽和高分别为 595, 848int width = 595;int height = 848;// 设置边距,单位是像素,10mm边距,对应 28pxint marginLeft = 10;int marginRight = 0;int marginTop = 10;int marginBottom = 0;if(printerName.contains("bar")) {// 云南大学条码纸张规格70mm宽*40mm高,对应像素值为 198, 113width = 198;height = 113;}paper.setSize(width, height);// 下面一行代码,解决了打印内容为空的问题paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom));return paper;}

请务必注意调用Paper对象的setImageableArea方法,否则你将很惊喜地看到打印内容一片空白的现象。

4、Book,顾名思义,书,可以看做一个有多页的册子,每一页都可以有自己不同的纸张格式。PDFPrintable就是用于它的

append方法的第一个参数。append方法源代码如下:

/*** Appends <code>numPages</code> pages to the end of this* <code>Book</code>.  Each of the pages is associated with* <code>page</code>.* @param painter   the <code>Printable</code> instance that renders*                  the page* @param page      the size and orientation of the page* @param numPages  the number of pages to be added to the*                  this <code>Book</code>.* @throws NullPointerException*          If the <code>painter</code> or <code>page</code>*          argument is <code>null</code>*/public void append(Printable painter, PageFormat page, int numPages) {BookPage bookPage = new BookPage(painter, page);int pageIndex = mPages.size();int newSize = pageIndex + numPages;mPages.setSize(newSize);for(int i = pageIndex; i < newSize; i++){mPages.setElementAt(bookPage, i);}}

book.append(pdfPrintable, pageFormat, document.getNumberOfPages()) 可实现打印多张,倘若用的它的没有第三个参数重载方法public void append(Printable painter, PageFormat page),那么你将惊喜地看到明明选择了三张,却只打印了一张的现象。

倘若代码里未曾设置纸张格式,则可能会看到内容截断、跨两页纸、纸张横向内容纵向等奇怪现象。

因为java默认的打印,会从打印机纸张里寻找相近的纸张进行匹配,如果没有添加自定义纸张,可能找出来的是别的纸张,而且,java读取纸张有个限制, 那就是默认纸张 高度 >= 宽度,高度 < 宽度的纸张是读取不到的。

原因在源代码里,请看,

/*** {@inheritDoc}* * Returns the actual physical size of the pages in the PDF file. May not fit the local printer.*/@Overridepublic PageFormat getPageFormat(int pageIndex){PDPage page = document.getPage(pageIndex);PDRectangle mediaBox = PDFPrintable.getRotatedMediaBox(page);PDRectangle cropBox = PDFPrintable.getRotatedCropBox(page);// Java does not seem to understand landscape paper sizes, i.e. where width > height, it// always crops the imageable area as if the page were in portrait. I suspect that this is// a JDK bug but it might be by design, see PDFBOX-2922.//// As a workaround, we normalise all Page(s) to be portrait, then flag them as landscape in// the PageFormat.Paper paper;boolean isLandscape;if (mediaBox.getWidth() > mediaBox.getHeight()){// rotatepaper = new Paper();paper.setSize(mediaBox.getHeight(), mediaBox.getWidth());paper.setImageableArea(cropBox.getLowerLeftY(), cropBox.getLowerLeftX(),cropBox.getHeight(), cropBox.getWidth());isLandscape = true;}else{paper = new Paper();paper.setSize(mediaBox.getWidth(), mediaBox.getHeight());paper.setImageableArea(cropBox.getLowerLeftX(), cropBox.getLowerLeftY(),cropBox.getWidth(), cropBox.getHeight());isLandscape = false;}PageFormat format = new PageFormat();format.setPaper(paper);// auto portrait/landscapeswitch (orientation){case AUTO:format.setOrientation(isLandscape ? PageFormat.LANDSCAPE : PageFormat.PORTRAIT);break;case LANDSCAPE:format.setOrientation(PageFormat.LANDSCAPE);break;case PORTRAIT:format.setOrientation(PageFormat.PORTRAIT);break;default:break;}return format;}

这是PDFPageable重写的Book的方法,获取纸张格式,该方法在纸张宽度大于高度的时候进行了调换,将宽度给了高度,高度给了宽度,相当于顺时针或逆时针旋转了90度。我打印条码的时候就遇到了这个问题,条码规格是宽70毫米,高40毫米,打印机纸张是横向的,但是内容总是纵着出来,无论怎么设置,打印模板横纵切换,还是打印机打印方向横纵切换,都没有效果,就是这段代码搞的鬼。这是我忽略了设置纸张格式导致的,请大家今后务必注意。

当然,仅在代码里设置纸张格式是不够的,打印机里一定要确实有这种纸张格式才行。否则,打印机又会智能地寻找最相近的纸张来忽悠你了。

解决问题过程中,有参考如下博客文档,写的很好,推荐下。

http://www.cnblogs.com/xdecode/p/7905041.html

PDFBox打印PDF A4格式文档和定制规格条码实例相关推荐

  1. linux excel pdf文件大小,为什么PDF这种格式文档运用得这么广泛

    原标题:为什么PDF这种格式文档运用得这么广泛 PDF文件格式是现在最常用的文件格式之一,在各行各业都得到了广泛的运用.与其他常见文件格式如Word或PPT文档相比(再加上我们可以使用专业的PDF阅读 ...

  2. QT5 界面截图保存到本地+输出PDF/WORD格式文档+QT界面中文乱码及输出PDF中文乱码的解决(亲身实践并且成功)

    最近做了一个和QT5有关的项目,遇到很多问题也学习到不少,特意写下来希望帮到更多的人.(我的版本VS2017+QT5.12.0) 一.QT5截图并保存到本地 在头文件添加必须项 #include &l ...

  3. php使用pdf2htmlex,转换 HTML 与 PDF 格式文档的神器

    企业 Web 项目开发中经常会有生产 PDF 格式文档的需求,例如 PDF 账单下载,月末生成各种统计报表等等.我们要帮助企业实现自动化,也就是说无需人工干预,程序能够按需从 DB 中拿数据自主生成. ...

  4. OFDRW提供了将OFD文档导出为其他格式文档的能力,如导出为图片、SVG、PDF、文本等

    OFDRW提供了将OFD文档导出为其他格式文档的能力,如导出为图片.SVG.PDF.文本等. OFDRW 转换模块在 2.0.0 之后抽象了多种文档导出接口,使用统一的 API 实现 OFD 文档导出 ...

  5. word文档转为PDF以及多种文档格式转换

    word文档转为PDF以及多种文档格式转换 项目地址:https://gitee.com/Jakewabc/word-of-pdf.git 相关案例: https://github.com/aspos ...

  6. 将PDF和Gutenberg文档格式转换为文本:生产中的自然语言处理

    Estimates state that 70%–85% of the world's data is text (unstructured data). Most of the English an ...

  7. Java DOC 转换给 PDF 格式文档的代码

    工作过程,把写代码过程经常用的代码片段备份一次,下面的代码段是关于Java DOC 转换给 PDF 格式文档的代码,应该对码农们有所用. import java.io.File; import jav ...

  8. 批量将 PDF 转为 ePub 格式文档

    概要:PDF 格式文档转为 ePub 格式的需求是我们经常会碰到的,网上的各种工具效果也是良莠不齐,经常会出现格式错乱.字体丢失等各种问题.今天给大家介绍的是一种非常简单的方式完成将多个 PDF 文档 ...

  9. 精确保留格式:将 PDF 转换为 Word 文档的技巧

    PDF 格式的文档广泛应用于各个领域,但是在进行编辑或修改时会受到一定限制,因此将 PDF 转换为 Word 文档就成为了很多人的需求.但是,将 PDF 转换为 Word 文档时往往会出现格式不一致或 ...

最新文章

  1. 用于自动驾驶的实时车道线检测和智能告警
  2. 《Nmap渗透测试指南》—第1章1.2节Windows下安装Nmap
  3. “口罩厂”霍尼韦尔搞出的量子计算机,刚刚获得了Nature认可
  4. 北京师范大学计算机系录取分数线,北京师范大学各省各专业录取分数线
  5. python画简单图片-Python 画个图
  6. 看徐坤的话剧《性情男女》
  7. 推荐系统图算法实用干货汇总(含论文、代码、样例教程)
  8. 搭载敏捷飞天底座,阿里云专有云敏捷版全面升级
  9. 计算机组装过程英文版,计算机组装与维护试题及答案(国外英文资料).doc
  10. 如何通过序列化在网络间传递对象,网络协议:轻松定义自己的网络通讯协议
  11. IDEA 值得推荐的十几款优秀插件,狂,拽,屌!
  12. 一篇文章助你深入理解zookeeper
  13. NAS DIY的设计和实施过程-5-Openfiler篇
  14. java实现小写转大写_人民币小写转大写(Java实现)
  15. 快乐暑假(八)——欧拉回路和哈密顿回路
  16. polybezier
  17. requests模块
  18. Python evel函数
  19. 女生适合学酒店管理 电子商务还是计算机,读中专是学旅游服务与酒店管理好呢?还是计算机好呢?这两个专业那个更好呢?...
  20. 玩转全球最大同性交友网站--- 开源社区GitHub

热门文章

  1. Java画UML类图
  2. 手机虚拟化--人人都只用手机了
  3. 【VINS-Mono】Estimator::initialStructure
  4. remote: Repository not found. fatal: repository ‘xxxxxxx‘ not found
  5. 小程序重构 [cnode社区]:mpvue + 开源api,现已上线!
  6. MySQL存储引擎InnoDB架构
  7. git 拉取远端分支
  8. python学习总结7 - 输入与输出【格式化字符串及读写文件】
  9. 《SQL经典实例》六——字符串处理
  10. 2.4G模块NRF24L01调试经验