【项目管理】Java使用pdfbox调用打印机打印PDF文件

系统:Win10
Java:1.8.0_333
IDEA:2020.3.4
Gitee:https://gitee.com/lijinjiang01/Printer

1.项目前言

在工作中,有次收到一位同事给我提的建议,说公司的CS系统,能不能直接调用打印机打印PDF,因为我们的系统之前是将表单导出PDF到本地,然后再打开打印,而他的工作有很大一部分是需要打印表单的,所以为了减轻工作量,向我提了这份需求申请,我先考虑了一下这个需求的实现难度,感觉应该是可以实现的,就答应了下来。

2.项目实现

为了实现这个功能,我先去网上查找了一下资料,对比了一下实现难度以及我们当前所处的环境(因为我们公司文件落地加密,所以直接调用打印机打印是不好实现的),我最终选择使用 Apache PDFbox 这个开源项目来实现 PDF 文件格式的打印。

Apache PDFbox 是一个开源的、基于 Java 的、支持 PDF 文档生成的工具库,它可以用于创建新的 PDF 文档,修改现有的 PDF 文档,还可以从 PDF 文档中提取所需的内容。Apache PDFBox 还包含了数个命令行工具。可以说,这个开源的工具库的功能是很强大的,在此,我们只研究打印功能。

Apache PDFbox 的优点:功能强大,代码开源,较完美的解决了 PDF 格式文件的一系列处理,使用方便。

3.关键代码

这里我们获取了本地的所有打印机服务,并将系统的默认打印机作为首选项

//获取本地的打印服务,并且设置默认打印机
private JComboBox<String> selectPrintService() {
    
    
    defaultPrintService = PrintServiceLookup.lookupDefaultPrintService();//获取默认打印机
    JComboBox<String> comboBox = new JComboBox<>();
    //获得本台电脑连接的所有打印机
    PrintService[] printServices = PrinterJob.lookupPrintServices();
    if (printServices == null || printServices.length == 0) {
    
    
        comboBox.addItem("获取本地打印机失败,请联系管理员!");
    } else {
    
    
        for (PrintService printService : printServices) {
    
    
            String value = printService.getName();
            serviceMap.put(value, printService);//将打印机名称及打印机服务添加到集合
            comboBox.addItem(value);
            //将默认打印机设置为下拉选的默认选择项
            if (defaultPrintService != null && defaultPrintService.getName().equals(value)) {
    
    
                comboBox.setSelectedItem(value);
            }
        }
    }
    return comboBox;
}
//打印功能实现
public void print(Container parent) {
    
    
    PDDocument document = null;
    File file;
    try {
    
    
        file = new File(filepath);
        if (file == null || !file.exists()) {
    
    
            JOptionPane.showMessageDialog(parent, "要打印的PDF文件不存在!", "警告", JOptionPane.WARNING_MESSAGE);
            return;
        }
        document = PDDocument.load(file);
        PrinterJob printerJob = PrinterJob.getPrinterJob();
        printerJob.setJobName(file.getName());
        printerJob.setPrintService(defaultPrintService);//设置打印机
        //设置纸张及缩放
        PDFPrintable pdfPrintable = new PDFPrintable(document, scaling);
        //设置多页打印
        Book book = new Book();
        PageFormat pageFormat = new PageFormat();
        //设置打印方向
        pageFormat.setOrientation(orientation);//纵向
        Paper paper = getPaper();
        pageFormat.setPaper(paper);
        book.append(pdfPrintable, pageFormat, document.getNumberOfPages());
        printerJob.setPageable(book);
        printerJob.setCopies(copies);//设置打印份数
        //添加打印属性
        HashPrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
        attributes.add(sides); //设置单双页
        attributes.add(MediaSizeName.ISO_A4);//默认A4纸打印
        printerJob.print(attributes);

    } catch (Exception e) {
    
    
        e.printStackTrace();
    } finally {
    
    
        if (document != null) {
    
    
            try {
    
    
                document.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

private Paper getPaper() {
    
    
    Paper paper = new Paper();
    // 默认为A4纸张,对应像素宽和高分别为 595, 842
    int width = 595;
    int height = 842;
    // 设置边距,单位是像素,10mm边距,对应 28px
    int marginLeft = 12;
    int marginRight = 12;
    int marginTop = 12;
    int marginBottom = 12;
    paper.setSize(width, height);
    // 下面一行代码,解决了打印内容为空的问题
    paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom));
    return paper;
}

4.效果演示

运行之后,会弹出类似这个的界面,简单配置下(这里的参数都是默认的,一般用这些就可以直接打印),点击打印,就会将文件传送到打印机服务的打印队列进行打印
在这里插入图片描述
我这里选择了 Microsoft Print to PDF 测试一下
在这里插入图片描述
这里我们发现使用pdfbox打印出来的PDF的字体发生了改变,打印的结果和原PDF不一致
在这里插入图片描述

5.问题处理

这里我们去控制台看一下为什么会出现这个问题,在这里我们不难发现导致这个问题的原因在于:原PDF中用到了一种 STSong-Light 的字体,而我们系统没有安装这个字体,所以他就自动给我们换了一个字体,难怪变得这么丑了,这里我们给出两种解决办法
在这里插入图片描述

5.1 安装对应字体

因为程序检测到我们没有安装这种字体,我们只需要将对应字体安装上去,然后重启电脑,应该就能解决这个问题

5.2 修改对应代码

因为我们不可能给每个用户加装一个字体,所以我们只能从程序出发

我们去交友网站上找到该项目的源码地址:https://github.com/apache/pdfbox/tree/trunk/pdfbox,本来想用源码的,不过可能版本不同,会报错。

所以我们可以在 pdfbox 的 org.apache.pdfbox.pdmodel.font 路径下找到 FontMapperImpl.class 文件,然后把他的反编译代码复制到我们项目中(记得先创建路径文件夹)
然后在字体映射集合中添加所缺字体的映射字体(映射的字体选择系统中有的,且和所缺字体近似的字体)就可以了

//添加STSong-Light字体映射
this.substitutes.put("STSong-Light", Arrays.asList("STSong-Light", "SimSun", "SIMFANG", "STFangsong"));

在这里插入图片描述
然后再重试下,就发现可以正常打印了
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_35132089/article/details/128005123