• 【项目管理】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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    //打印功能实现
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    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"));
    
    • 1
    • 2

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

  • 相关阅读:
    基于图搜索的规划算法之A*家族(六):D* Lite算法
    N 叉树的后序遍历
    【Axure高保真原型】桥梁监控大屏可视化案例
    【小记】二八十十六,进制团团转
    关于《web课程设计》网页设计 用html css做一个漂亮的网站 仿新浪微博个人主页
    复习三:线性表
    大语言模型LLM知多少?
    Verilog写状态机的三种描述方式之二段式
    poi+ResultSet+线程池导出数据库表结构
    Java的三大特性-继承
  • 原文地址:https://blog.csdn.net/qq_35132089/article/details/128005123