SpringBoot+Vue项目的PDF导出及给PDF文件盖章的功能示例

前言

版本:
SpringBoot:2.3.1.RELEASE
itextpdf:5.4.3

最近在做SpringBoot+Vue的项目,需要将委托单商品的数据导出为PDF文档。Java的PDF操作第三方工具类用的最多的应该是itextpdf了吧,当然以前也用过icepdf这种小众的。那现在就来看看用itextpdf怎么做,还是很简单的。
首先前端页面如下,可以导出箱单和发票两种格式的PDF文件:
在这里插入图片描述

一、引入itextpdf依赖
<!--PDF导出-->
<dependency>
	<groupId>com.itextpdf</groupId>
	<artifactId>itextpdf</artifactId>
	<version>5.4.3</version>
</dependency>

只需要这一个包就可以了。网上其他教程引了一大堆包,完全没必要。

二、主要业务代码
导出发票
 /**
     * 导出PDF格式发票
     *
     * @param id
     * @return io.zbus.transport.Message
     * @author ZHANGCHAO
     * @date 2021/1/26 13:42
     **/
    public Message exportCommercialInvoiceOfPdf(String id) {
        Apply apply = baseMapper.selectById(id);
        if (isEmpty(apply)) {
            return null;
        }
        String tenantId = RequestKit.getRequestIn().getHeader(TENANT_ID);
        Message res = new Message();
        RequestKit.copyHeaders(false, TENANT_ID, HEADER_TOKEN, TENANT_TYPE);
        List<ApplyInvoices> applyInvoicesList = applyInvoicesMapper.selectList(new QueryWrapper<ApplyInvoices>().lambda()
                .eq(ApplyInvoices::getApplyNumber, id)
                .orderByAsc(ApplyInvoices::getSequence));
        apply.setApplyInvoicesList(applyInvoicesList);
        // pdf文件名
        String fileName = "发票_" + (isNotBlank(apply.getInvoiceNo()) ? apply.getInvoiceNo() : "无发票号") + ".pdf";
        String path = ProjectPath.getExportPdfPath() + File.separator;
        File file = new File(path);
        if (!file.exists()) { // 如果文件夹不存在
            file.mkdir(); // 创建文件夹
        }
        String filePath = path + fileName;
        /*****************创建PDF开始*********************/
        /** 实例化文档对象 */
        Document document = new Document(PageSize.A4);
        log.info("A4的宽度:" + PageSize.A4.getWidth() + "高度:" + PageSize.A4.getHeight());
        try {
            /** 创建 PdfWriter 对象 */// 文档对象的引用
            // 为document创建一个监听,并把PDF流写到文件中
            PdfWriter.getInstance(document, new FileOutputStream(filePath)); // 文件的输出路径+文件的实际名称
            document.open();// 打开文档

//            // 解决中文不显示问题
//            BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
//            // 设置字体大小
//            Font fontChina18_BOLD = new Font(bfChinese, 18, Font.BOLD);
//            Font fontChina12 = new Font(bfChinese, 12);
//            Font fontChina12_BOLD = new Font(bfChinese, 12, Font.BOLD);
//            Font fontChina8 = new Font(bfChinese, 8);//加粗 Font.BOLD
//            Font fontChina8_BOLD = new Font(bfChinese, 8, Font.BOLD); // 加粗 Font.BOLD
            //英文下字体
//            Font fontContent = new Font(Font.FontFamily.HELVETICA, 18, Font.BOLD|Font.ITALIC);
            // 用自己的字体
            BaseFont bf = BaseFont.createFont(ProjectPath.getFontPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            Font titleFont = new Font(bf, 20, Font.BOLDITALIC); // 24号加粗斜体
            // 空格代码
            Paragraph blank = new Paragraph(" ");

            /**** 向文档添加内容 ***/
            // 标题
            String title = "";
            String kehuAddress = "";
            String kehuTelFax = "";
            // 境外收发货人抬头信息
            if (isNotBlank(apply.getConsignee())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignee().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getCustomerAddressList())) {
                    title = getHeaderInfo(customer.getCustomerAddressList());
                    CustomerAddress ca = getAddress(customer.getCustomerAddressList());
                    if (isNotEmpty(ca)) {
                        kehuAddress = ca.getAddress();
                        kehuTelFax = "TEL:" + ca.getPhone() + " FAX:" + ca.getFax();
                    }
                }
            }
            Paragraph titleParagraph = new Paragraph(title, titleFont);
            document.add(titleParagraph); // 文档标题
            document.add(blank);
            Paragraph addressParagraph = new Paragraph(kehuAddress, new Font(bf, 15, Font.NORMAL));
            Paragraph telFaxParagraph = new Paragraph(kehuTelFax, new Font(bf, 15, Font.NORMAL));
            document.add(addressParagraph); // 客户地址
            document.add(telFaxParagraph); // 客户电话+传真
            document.add(blank);
            document.add(blank);
            document.add(blank);
            // 画横线
            //1.线宽度
            //2.直线长度,是个百分百,0-100之间
            //3.直线颜色
            //4.直线位置
            //5.上下移动位置
            LineSeparator line = new LineSeparator(2f, 100, BaseColor.BLACK, Element.ALIGN_CENTER, 0f);
            document.add(line); // 画横线
            Paragraph invoiceParagraph = new Paragraph("INVOICE", new Font(bf, 20, Font.BOLD));
            invoiceParagraph.setSpacingBefore(20);
            document.add(invoiceParagraph); // invoice
            document.add(blank);
            Paragraph billTo = new Paragraph("Bill To", new Font(bf, 12, Font.BOLD));
            document.add(billTo); // billto
            // 承运商委托单位企业信息
            String headerInfo = "";
            String address = "";
            String telFax = "";
            if (isNotBlank(apply.getConsignor())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignor().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getCustomerAddressList())) {
                    headerInfo = getHeaderInfo(customer.getCustomerAddressList());
                    CustomerAddress ca = getAddress(customer.getCustomerAddressList());
                    if (isNotEmpty(ca)) {
                        address = ca.getAddress();
                        telFax = "Tel:" + ca.getPhone() + " Fax:" + ca.getFax();
                    }
                }
            }
            Paragraph headerInfoParagraph = new Paragraph(headerInfo, new Font(bf, 12, Font.NORMAL));
            headerInfoParagraph.setIndentationRight(PageSize.A4.getWidth() / 2 - 30);
            Paragraph addressParagraph_ = new Paragraph(address, new Font(bf, 12, Font.NORMAL));
            Paragraph telFaxParagraph_ = new Paragraph(telFax, new Font(bf, 12, Font.NORMAL));
            document.add(headerInfoParagraph);
            document.add(addressParagraph_);
            document.add(telFaxParagraph_);
            // 2021/2/1 13:57@ZHANGCHAO 追加/变更/完善:获取签名地址!!
//            if (isNotBlank(apply.getConsignor())) {
//                Customer customer = orderService.getCustomerByName(apply.getConsignor().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
//                if (isNotEmpty(customer) && isNotEmpty(customer.getAttachmentList())) {
//                    // 获取已绑定的表单图片!
//                    List<Attachment> attachments = customer.getAttachmentList().stream()
//                            .filter(attachment -> "0".equals(attachment.getSubType())
//                                    && "1".equals(attachment.getStatus())).collect(Collectors.toList());
//                    if (isNotEmpty(attachments)) {
//                        Image yinzhang = Image.getInstance(ProjectPath.getImgPath() + attachments.get(0).getPath());
//                        yinzhang.setAlignment(Image.ALIGN_UNDEFINED);
                        yinzhang.scalePercent(75); // 依照比例缩放
                      Image类方法:
//                        yinzhang.scaleAbsolute(120, 120); // 将图像缩放到绝对宽度和绝对高度。
                        scaleAbsoluteHeight(float newHeight); // 将图像缩放到绝对高度。
                        scaleAbsoluteWidth(float newWidth); //将图像缩放到绝对宽度。
                        scalePercent(float percent); // 将图像缩放到一定百分比。
                        scalePercent(float percentX, float percentY); //将图像的宽度和高度缩放到一定百分比。
                        scaleToFit(float fitWidth, float fitHeight); //缩放图像,使其适合特定的宽度和高度。
                        yinzhang.scaleToFit(120, 120); // 依照比例缩放
//                        yinzhang.setSpacingBefore(200);
//                        yinzhang.setIndentationRight(150);
//                        document.add(yinzhang);
//                    }
//                }
//            }
            document.add(blank);
            // 2021/2/1 14:38@ZHANGCHAO 追加/变更/完善:获取供应商的签名章!!
            // 境外收发货人抬头信息
            if (isNotBlank(apply.getConsignee())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignee().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getAttachmentList())) {
                    // 获取已绑定的表单图片!
                    List<Attachment> attachments = customer.getAttachmentList().stream()
                            .filter(attachment -> "0".equals(attachment.getSubType())
                                    && "1".equals(attachment.getStatus())).collect(Collectors.toList());
                    if (isNotEmpty(attachments)) {
                        Image sign = Image.getInstance(ProjectPath.getImgPath() + attachments.get(0).getPath());
                        sign.setAlignment(Image.ALIGN_RIGHT);
//                        sign.scalePercent(75); // 依照比例缩放
                        sign.scaleToFit(120, 120); // 依照比例缩放
                        sign.setIndentationRight(50);
                        document.add(sign);
                    }
                }
            }
            document.add(blank);
            LineSeparator line1 = new LineSeparator(1f, 100, BaseColor.BLACK, Element.ALIGN_CENTER, 0f);
            document.add(line1); // 画横线
            Paragraph pg = new Paragraph("TERMS OF DELIVERY", new Font(bf, 13, Font.NORMAL));
            document.add(pg);
            // 转化成交方式
            String transMode = "";
            if (isNotBlank(apply.getTransMode())) {
                DictQuery dictQuery = baseMapper.getDictInfo("CJFS", apply.getTransMode());
                if (isNotEmpty(dictQuery)) {
                    transMode = dictQuery.getText();
                }
            }
            Paragraph transModePG = new Paragraph(transMode, new Font(bf, 13, Font.NORMAL));
            transModePG.setIndentationLeft(80);
            document.add(transModePG);
            Paragraph pg1 = new Paragraph("THIS PROFORMA INVOICE IS NOT ASSIGNABLE OR NEGOTIABLE", new Font(bf, 13, Font.ITALIC));
            document.add(pg1);
            Paragraph pg2 = new Paragraph("SUBJECT TO DELIVERY WHEN AVAILABLE AND AT PRICE IN EFFECT AT DATE OF SHIPMENT.", new Font(bf, 13, Font.ITALIC));
            document.add(pg2);
            // 处理表格
            if (isNotEmpty(applyInvoicesList)) {
                List<String> invoiceList = createInvoiceList(applyInvoicesList);
                float[] columnWidths = {1.2f, // 件号
                        1.5f, // 英文品名
                        1f, // 数量
                        0.8f, // 单位
                        1.1f, // 币制
                        1.1f, // 单价
                        1.5f, // 总价
                }; // 设置表格列宽参数
                getCiTableList(document, 7, columnWidths, invoiceList, applyInvoicesList, "CI", 100,
                        new Font(bf, 13, Font.BOLD), new Font(bf, 13, Font.NORMAL));
            }
            // 关闭文档
            document.close();
            /*****************创建PDF结束*********************/
            // 2021/2/8 10:53@ZHANGCHAO 追加/变更/完善:设置印章!!
            boolean flag = false;
            String targetPath = "";
            if (isNotBlank(apply.getConsignor())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignor().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getAttachmentList())) {
                    // 获取已绑定的表单图片!
                    List<Attachment> attachments = customer.getAttachmentList().stream()
                            .filter(attachment -> "0".equals(attachment.getSubType())
                                    && "1".equals(attachment.getStatus())).collect(Collectors.toList());
                    if (isNotEmpty(attachments)) {
                        // 读取模板文件
                        targetPath = path + "1-" + fileName;
                        InputStream input = new FileInputStream(filePath);
                        PdfReader reader = new PdfReader(input);
                        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(targetPath));
                        // 获取操作的页面
                        PdfContentByte under = stamper.getOverContent(1);
                        Image image = Image.getInstance(ProjectPath.getImgPath() + attachments.get(0).getPath());
                        // 根据域的大小缩放图片
                        image.scaleToFit(120, 120);
                        // 添加图片
                        // 获取关键字的坐标
                        List<float[]> positions = PDFUtil.findKeywordPostions(new FileInputStream(filePath), "Bill To");
                        System.out.println("total:" + positions.size());
                        if (isNotEmpty(positions)) {
                            for (float[] position : positions) {
                                System.out.print("pageNum: " + (int) position[0]);
                                System.out.print("\tx: " + position[1]);
                                System.out.println("\ty: " + position[2]);
                            }
                        }
//                        image.setAbsolutePosition(PageSize.A4.getWidth() / 3 - 95, PageSize.A4.getHeight() / 2 + 60);
                        image.setAbsolutePosition(positions.get(0)[1] + 70, positions.get(0)[2] - 120);
                        under.addImage(image);
                        stamper.close();
                        reader.close();
                        flag = true;
                    }
                }
            }
            String contentType = "application/octet-stream";
            res.setHeader(Http.CONTENT_TYPE, contentType);
            res.setStatus(RspCode.SUCCESS);
            // 将文件转为字节流输出
            if (flag) {
                res.setBody(fileConvertToByteArray(new File(targetPath)));
                // 删除文件
                FileUtil.deleteFile(filePath);
                FileUtil.deleteFile(targetPath);
            } else {
                res.setBody(fileConvertToByteArray(new File(filePath)));
                // 删除文件
                FileUtil.deleteFile(filePath);
            }
            return res;
        } catch (Exception e) {
            e.printStackTrace();
            ExceptionUtil.getFullStackTrace(e);
        }
        return null;
    }
导出箱单
 /**
     * 导出PDF格式箱单
     *
     * @param id
     * @return io.zbus.transport.Message
     * @author ZHANGCHAO
     * @date 2021/1/26 13:43
     **/
    public Message exportPackingListOfPdf(String id) {
        Apply apply = baseMapper.selectById(id);
        if (isEmpty(apply)) {
            return null;
        }
        String tenantId = RequestKit.getRequestIn().getHeader(TENANT_ID);
        Message res = new Message();
        RequestKit.copyHeaders(false, TENANT_ID, HEADER_TOKEN, TENANT_TYPE);
        List<ApplyInvoices> applyInvoicesList = applyInvoicesMapper.selectList(new QueryWrapper<ApplyInvoices>().lambda()
                .eq(ApplyInvoices::getApplyNumber, id)
                .orderByAsc(ApplyInvoices::getSequence));
        apply.setApplyInvoicesList(applyInvoicesList);
        // pdf文件名
        String fileName = "箱单_" + (isNotBlank(apply.getInvoiceNo()) ? apply.getInvoiceNo() : "无发票号") + ".pdf";
        String path = ProjectPath.getExportPdfPath() + File.separator;
        File file = new File(path);
        if (!file.exists()) { // 如果文件夹不存在
            file.mkdir(); // 创建文件夹
        }
        String filePath = path + fileName;
        /*****************创建PDF开始*********************/
        /** 实例化文档对象 */
        Document document = new Document(PageSize.A4);
        log.info("A4的宽度:" + PageSize.A4.getWidth() + "高度:" + PageSize.A4.getHeight());
        try {
            /** 创建 PdfWriter 对象 */// 文档对象的引用
            // 为document创建一个监听,并把PDF流写到文件中
            PdfWriter.getInstance(document, new FileOutputStream(filePath)); // 文件的输出路径+文件的实际名称
            document.open();// 打开文档
            // 用自己的字体
            BaseFont bf = BaseFont.createFont(ProjectPath.getFontPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            Font titleFont = new Font(bf, 20, Font.BOLDITALIC); // 24号加粗斜体
            // 空格代码
            Paragraph blank = new Paragraph(" ");
            /**** 向文档添加内容 ***/
            // 标题
            String title = "";
            String kehuAddress = "";
            String kehuTelFax = "";
            // 境外收发货人抬头信息
            if (isNotBlank(apply.getConsignee())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignee().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getCustomerAddressList())) {
                    title = getHeaderInfo(customer.getCustomerAddressList());
                    CustomerAddress ca = getAddress(customer.getCustomerAddressList());
                    if (isNotEmpty(ca)) {
                        kehuAddress = ca.getAddress();
                        kehuTelFax = "TEL:" + ca.getPhone() + " FAX:" + ca.getFax();
                    }
                }
            }
            Paragraph titleParagraph = new Paragraph(title, titleFont);
            document.add(titleParagraph); // 文档标题
            document.add(blank);
            Paragraph addressParagraph = new Paragraph(kehuAddress, new Font(bf, 15, Font.NORMAL));
            Paragraph telFaxParagraph = new Paragraph(kehuTelFax, new Font(bf, 15, Font.NORMAL));
            document.add(addressParagraph); // 客户地址
            document.add(telFaxParagraph); // 客户电话+传真
            document.add(blank);
            document.add(blank);
            Paragraph invoiceParagraph = new Paragraph("Packing list", new Font(bf, 20, Font.BOLD));
            invoiceParagraph.setSpacingBefore(20);
            document.add(invoiceParagraph); // invoice
            document.add(blank);
            // 2021/2/1 13:57@ZHANGCHAO 追加/变更/完善:获取签名地址!!
//            if (isNotBlank(apply.getConsignor())) {
//                Customer customer = orderService.getCustomerByName(apply.getConsignor().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
//                if (isNotEmpty(customer) && isNotEmpty(customer.getAttachmentList())) {
//                    // 获取已绑定的表单图片!
//                    List<Attachment> attachments = customer.getAttachmentList().stream()
//                            .filter(attachment -> "0".equals(attachment.getSubType())
//                                    && "1".equals(attachment.getStatus())).collect(Collectors.toList());
//                    if (isNotEmpty(attachments)) {
//                        Image yinzhang = Image.getInstance(ProjectPath.getImgPath() + attachments.get(0).getPath());
//                        yinzhang.setAlignment(Image.ALIGN_UNDEFINED);
//                        yinzhang.scalePercent(75); // 依照比例缩放
//                        yinzhang.setIndentationRight(150);
//                        document.add(yinzhang);
//                    }
//                }
//            }
            Paragraph billTo = new Paragraph("Bill To", new Font(bf, 12, Font.BOLD));
            document.add(billTo); // billto
            // 承运商委托单位企业信息
            String headerInfo = "";
            String address = "";
            String telFax = "";
            if (isNotBlank(apply.getConsignor())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignor().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getCustomerAddressList())) {
                    headerInfo = getHeaderInfo(customer.getCustomerAddressList());
                    CustomerAddress ca = getAddress(customer.getCustomerAddressList());
                    if (isNotEmpty(ca)) {
                        address = ca.getAddress();
                        telFax = "Tel:" + ca.getPhone() + " Fax:" + ca.getFax();
                    }
                }
            }
            Paragraph headerInfoParagraph = new Paragraph(headerInfo, new Font(bf, 12, Font.NORMAL));
            headerInfoParagraph.setIndentationRight(PageSize.A4.getWidth() / 2 - 30);
            Paragraph addressParagraph_ = new Paragraph(address, new Font(bf, 12, Font.NORMAL));
            Paragraph telFaxParagraph_ = new Paragraph(telFax, new Font(bf, 12, Font.NORMAL));
            document.add(headerInfoParagraph);
            document.add(addressParagraph_);
            document.add(telFaxParagraph_);
            document.add(blank);
            // 2021/2/1 14:38@ZHANGCHAO 追加/变更/完善:获取供应商的签名章!!
            // 境外收发货人抬头信息
            if (isNotBlank(apply.getConsignee())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignee().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getAttachmentList())) {
                    // 获取已绑定的表单图片!
                    List<Attachment> attachments = customer.getAttachmentList().stream()
                            .filter(attachment -> "0".equals(attachment.getSubType())
                                    && "1".equals(attachment.getStatus())).collect(Collectors.toList());
                    if (isNotEmpty(attachments)) {
                        Image sign = Image.getInstance(ProjectPath.getImgPath() + attachments.get(0).getPath());
                        sign.setAlignment(Image.ALIGN_RIGHT);
//                        sign.scalePercent(75); // 依照比例缩放
                        sign.scaleToFit(120, 120); // 依照比例缩放
                        sign.setIndentationRight(50);
                        document.add(sign);
                    }
                }
            }
            document.add(blank);

            // 处理表格
            if (isNotEmpty(applyInvoicesList)) {
                PdfPTable table = new PdfPTable(6);// 设置列数
                table.setSpacingBefore(20);
                table.setWidthPercentage(100);// 表格宽度为100%
                float[] columnWidths = {1.5f, // 件号
                        1.5f, // 英文品名
                        1.1f, // 数量
                        1.2f, // 净重
                        1.3f, // 毛重
                        1.3f, // 包装数
                }; // 设置表格列宽参数
                table.setWidths(columnWidths);
                /*表头*/
                getPlTableList(table, apply, new Font(bf, 13, Font.BOLD), new Font(bf, 13, Font.NORMAL));
                document.add(table);
                document.add(blank);
            }
            String lastStr = "";
            String packStr = "";
            if (isNotBlank(apply.getPacksKinds())) {
                DictQuery dictQuery = baseMapper.getDictInfo("BZZL", apply.getPacksKinds());
                if (isNotEmpty(dictQuery)) {
                    packStr = dictQuery.getTable();
                }
            }
            lastStr = "TOTAL ON " + apply.getPacks() + " " + (isNotBlank(packStr) ? packStr : "");
            Paragraph last = new Paragraph(lastStr, new Font(bf, 12, Font.BOLD));
            document.add(last); // billto

            // 关闭文档
            document.close();
            /*****************创建PDF结束*********************/
            // 2021/2/8 10:54@ZHANGCHAO 追加/变更/完善:设置印章!!
            boolean flag = false;
            String targetPath = "";
            if (isNotBlank(apply.getConsignor())) {
                Customer customer = orderService.getCustomerByName(apply.getConsignor().trim(), tenantId, isNotEmpty(apply.getConsignorId()) ? apply.getConsignorId().toString() : null);
                if (isNotEmpty(customer) && isNotEmpty(customer.getAttachmentList())) {
                    // 获取已绑定的表单图片!
                    List<Attachment> attachments = customer.getAttachmentList().stream()
                            .filter(attachment -> "0".equals(attachment.getSubType())
                                    && "1".equals(attachment.getStatus())).collect(Collectors.toList());
                    if (isNotEmpty(attachments)) {
                        // 读取模板文件
                        targetPath = path + "1-" + fileName;
                        InputStream input = new FileInputStream(filePath);
                        PdfReader reader = new PdfReader(input);
                        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(targetPath));
                        // 获取操作的页面
                        PdfContentByte under = stamper.getOverContent(1);
                        Image image = Image.getInstance(ProjectPath.getImgPath() + attachments.get(0).getPath());
                        // 根据域的大小缩放图片
                        image.scaleToFit(120, 120);
                        // 添加图片
                        // 获取关键字的坐标
                        List<float[]> positions = PDFUtil.findKeywordPostions(new FileInputStream(filePath), "Bill To");
                        System.out.println("total:" + positions.size());
                        if (isNotEmpty(positions)) {
                            for (float[] position : positions) {
                                System.out.print("pageNum: " + (int) position[0]);
                                System.out.print("\tx: " + position[1]);
                                System.out.println("\ty: " + position[2]);
                            }
                        }
//                        image.setAbsolutePosition(PageSize.A4.getWidth() / 3 - 95, PageSize.A4.getHeight() / 2 + 60);
                        image.setAbsolutePosition(positions.get(0)[1] + 70, positions.get(0)[2] - 120);
                        under.addImage(image);
                        stamper.close();
                        reader.close();
                        flag = true;
                    }
                }
            }
            String contentType = "application/octet-stream";
            res.setHeader(Http.CONTENT_TYPE, contentType);
            res.setStatus(RspCode.SUCCESS);
            // 将文件转为字节流输出
            if (flag) {
                res.setBody(fileConvertToByteArray(new File(targetPath)));
                // 删除文件
                FileUtil.deleteFile(filePath);
                FileUtil.deleteFile(targetPath);
            } else {
                res.setBody(fileConvertToByteArray(new File(filePath)));
                // 删除文件
                FileUtil.deleteFile(filePath);
            }
            return res;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
其他一些方法:
    /**
     * 把一个文件转化为byte字节数组。
     *
     * @return
     */
    private byte[] fileConvertToByteArray(File file) {
        byte[] data = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            data = bos.toByteArray();
            fis.close();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }

    /**
     * 按一定规则获取客户抬头信息
     * <p>
     * 委托单导出箱单发票,获取标签为1 的地址联系人。(如果找不到取0通用地址,如果没有则取默认地址,如果没有默认取第一条)
     *
     * @param customerAddressList
     * @return java.lang.String
     * @author ZHANGCHAO
     * @date 2021/1/19 9:33
     **/
    private String getHeaderInfo(List<CustomerAddress> customerAddressList) {
        String headerInfo;
        // 1.取对应
        List<CustomerAddress> duiyings = customerAddressList.stream()
                .filter(customerAddress -> "1".equals(customerAddress.getType())).collect(Collectors.toList());
        if (isNotEmpty(duiyings)) {
            headerInfo = duiyings.get(0).getHeaderInfo();
        } else {
            // 2.取通用
            List<CustomerAddress> tongyongs = customerAddressList.stream()
                    .filter(customerAddress -> "0".equals(customerAddress.getType())).collect(Collectors.toList());
            if (isNotEmpty(tongyongs)) {
                headerInfo = tongyongs.get(0).getHeaderInfo();
            } else {
                // 3.取默认
                List<CustomerAddress> morens = customerAddressList.stream()
                        .filter(CustomerAddress::getIsDefault).collect(Collectors.toList());
                if (isNotEmpty(morens)) {
                    headerInfo = morens.get(0).getHeaderInfo();
                } else {
                    // 4.取第一条
                    headerInfo = customerAddressList.get(0).getHeaderInfo();
                }
            }
        }
        return headerInfo;
    }

    /**
     * 按一定规则获取客户地址信息
     * <p>
     * 委托单导出箱单发票,获取标签为1 的地址联系人。(如果找不到取0通用地址,如果没有则取默认地址,如果没有默认取第一条)
     *
     * @param customerAddressList
     * @return java.lang.String
     * @author ZHANGCHAO
     * @date 2021/1/19 9:33
     **/
    private CustomerAddress getAddress(List<CustomerAddress> customerAddressList) {
        CustomerAddress ca;
        // 1.取对应
        List<CustomerAddress> duiyings = customerAddressList.stream()
                .filter(customerAddress -> "1".equals(customerAddress.getType())).collect(Collectors.toList());
        if (isNotEmpty(duiyings)) {
            ca = duiyings.get(0);
        } else {
            // 2.取通用
            List<CustomerAddress> tongyongs = customerAddressList.stream()
                    .filter(customerAddress -> "0".equals(customerAddress.getType())).collect(Collectors.toList());
            if (isNotEmpty(tongyongs)) {
                ca = tongyongs.get(0);
            } else {
                // 3.取默认
                List<CustomerAddress> morens = customerAddressList.stream()
                        .filter(CustomerAddress::getIsDefault).collect(Collectors.toList());
                if (isNotEmpty(morens)) {
                    ca = morens.get(0);
                } else {
                    // 4.取第一条
                    ca = customerAddressList.get(0);
                }
            }
        }
        return ca;
    }

主要思路是先根据委托单的流水号,查询出其下的商品数据,然后写入生成的PDF文件中。再读取生成的PDF文件,根据关键字“Bill To”的坐标插入印章,最后转为字节流输出给前端下载。

1. 处理商品数据的createInvoiceList方法:
 /**
     * 处理商品数据
     *
     * @param applyInvoicesList
     * @return java.util.List<java.lang.String>
     * @author ZHANGCHAO
     * @date 2021/1/27 15:28
     **/
    public static List<String> createInvoiceList(List<ApplyInvoices> applyInvoicesList) {
        List<String> invoiceList = new ArrayList<>();
        /****标题行****/
        invoiceList.add("Part No.");
        invoiceList.add("DESCRIPTION");
        invoiceList.add("QTY.");
        invoiceList.add("UNIT");
        invoiceList.add("CURRENCY");
        invoiceList.add("Unit price");
        invoiceList.add("AMOUNT");
        /****每行的值****/
        applyInvoicesList.forEach(applyInvoices -> {
            invoiceList.add(isNotBlank(applyInvoices.getPn()) ? applyInvoices.getPn() : null); // 件号物料号
            invoiceList.add(isNotBlank(applyInvoices.getEnName()) ? applyInvoices.getEnName() : null); // 英文品名
            invoiceList.add(isNotEmpty(applyInvoices.getQty()) ? applyInvoices.getQty().toString() : null); // 数量
            invoiceList.add(isNotBlank(applyInvoices.getQunit()) ? applyInvoices.getQunit() : null); // 单位
            invoiceList.add(isNotBlank(applyInvoices.getCurrency()) ? applyInvoices.getCurrency() : null); // 币制
            invoiceList.add(isNotEmpty(applyInvoices.getDcluprcamt()) ? applyInvoices.getDcluprcamt().toString() : null); // 单价
            invoiceList.add(isNotEmpty(applyInvoices.getValue()) ? applyInvoices.getValue().toString() : null); // 总价
        });
        return invoiceList;
    }
2. 获取表格数据getCiTableList方法代码:
 /**
     * 创建表格
     *
     * @param document
     * @param colspan
     * @param columnWidths
     * @param list
     * @param widthPercentage
     * @param headFont
     * @param childFont
     * @return void
     * @author ZHANGCHAO
     * @date 2021/1/27 15:44
     **/
    public static void getCiTableList(Document document, int colspan, float[] columnWidths, List<String> list,
                                       List<ApplyInvoices> applyInvoices, String type,
                                       float widthPercentage, Font headFont, Font childFont) throws DocumentException {
        PdfPTable table = new PdfPTable(colspan);// 设置列数
        table.setSpacingBefore(20);
        table.setWidthPercentage(widthPercentage);// 表格宽度为100%
        if (columnWidths != null) {//自定义列宽
            table.setWidths(columnWidths);
        }
        int number = 0;
        //设置打印行数
        for (int i = 0; i < list.size() / colspan; i++) {//打印条数 = 数据个数除以列数
            //每行每列生成一个单元格
            for (int j = 0; j < colspan; j++) { //打印列数
                PdfPCell cell = new PdfPCell(); //创建单元格
                cell.setMinimumHeight(30F);//表格高度
                cell.setUseAscender(true);
                cell.setHorizontalAlignment(Element.ALIGN_CENTER); //水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); //垂直居中
                if (i == 0) {//第一行标题栏
                    cell.setPhrase(new Paragraph(list.get(number), headFont));
                } else {
                    cell.setPhrase(new Paragraph(list.get(number), childFont));
                }
                table.addCell(cell);
                number++;
            }
        }
        // 处理最后一行
        if ("CI".equalsIgnoreCase(type)) {
            BigDecimal totalQty = new BigDecimal("0");
            BigDecimal totalAmount = new BigDecimal("0");
            for (ApplyInvoices a : applyInvoices) {
                totalQty = totalQty.add(isNotEmpty(a.getQty()) ? a.getQty() : new BigDecimal("0"));
                totalAmount = totalAmount.add(isNotEmpty(a.getValue()) ? a.getValue() : new BigDecimal("0"));
            }
            PdfPCell cell = new PdfPCell(); //创建单元格
            cell.setMinimumHeight(30F);//表格高度
            cell.setColspan(2);
            cell.setUseAscender(true); //开启单元格内文字位置设计
            cell.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
            cell.setPhrase(new Paragraph("TOTAL", headFont));
            table.addCell(cell);
            PdfPCell cell1 = new PdfPCell(); //创建单元格
            cell1.setMinimumHeight(30F);//表格高度
            cell1.setUseAscender(true); //开启单元格内文字位置设计
            cell1.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
            cell1.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
            cell1.setPhrase(new Paragraph(totalQty.toString(), headFont));
            table.addCell(cell1);
            PdfPCell cell2 = new PdfPCell(); //创建单元格
            cell2.setMinimumHeight(30F);//表格高度
            cell2.setUseAscender(true); //开启单元格内文字位置设计
            cell2.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
            cell2.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
            cell2.setPhrase(new Paragraph("", headFont));
            table.addCell(cell2);
            PdfPCell cell3 = new PdfPCell(); //创建单元格
            cell3.setMinimumHeight(30F);//表格高度
            cell3.setUseAscender(true); //开启单元格内文字位置设计
            cell3.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
            cell3.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
            cell3.setPhrase(new Paragraph("", headFont));
            table.addCell(cell3);
            PdfPCell cell4 = new PdfPCell(); //创建单元格
            cell4.setMinimumHeight(30F);//表格高度
            cell4.setUseAscender(true); //开启单元格内文字位置设计
            cell4.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
            cell4.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
            cell4.setPhrase(new Paragraph("", headFont));
            table.addCell(cell4);
            PdfPCell cell5 = new PdfPCell(); //创建单元格
            cell5.setMinimumHeight(30F);//表格高度
            cell5.setUseAscender(true); //开启单元格内文字位置设计
            cell5.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
            cell5.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
            cell5.setPhrase(new Paragraph(totalAmount.toString(), headFont));
            table.addCell(cell5);
        }
        document.add(table);
    }
3. 注意:箱单的话需要纵向合并单元格,而itextpdf的纵向合并简直是残废,这里需要单独处理下:
 /**
     * 创建箱单表格
     *
     * @param table
     * @param apply
     * @param headFont
     * @param childFont
     * @return void
     * @author ZHANGCHAO
     * @date 2021/1/28 10:53
     **/
    public static void getPlTableList(PdfPTable table, Apply apply, Font headFont, Font childFont) {
        PdfPCell cell = new PdfPCell(); //创建单元格
        cell.setMinimumHeight(30F);//表格高度
        cell.setUseAscender(true); //开启单元格内文字位置设计
        cell.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cell.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cell.setPhrase(new Paragraph("Part No.", headFont));
        table.addCell(cell);
        PdfPCell cell1 = new PdfPCell(); //创建单元格
        cell1.setMinimumHeight(30F);//表格高度
        cell1.setUseAscender(true); //开启单元格内文字位置设计
        cell1.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cell1.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cell1.setPhrase(new Paragraph("DESCRIPTION", headFont));
        table.addCell(cell1);
        PdfPCell cell2 = new PdfPCell(); //创建单元格
        cell2.setMinimumHeight(30F);//表格高度
        cell2.setUseAscender(true); //开启单元格内文字位置设计
        cell2.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cell2.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cell2.setPhrase(new Paragraph("QTY.", headFont));
        table.addCell(cell2);
        PdfPCell cell3 = new PdfPCell(); //创建单元格
        cell3.setMinimumHeight(30F);//表格高度
        cell3.setUseAscender(true); //开启单元格内文字位置设计
        cell3.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cell3.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cell3.setPhrase(new Paragraph("N.WT(Kg)", headFont));
        table.addCell(cell3);
        PdfPCell cell4 = new PdfPCell(); //创建单元格
        cell4.setMinimumHeight(30F);//表格高度
        cell4.setUseAscender(true); //开启单元格内文字位置设计
        cell4.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cell4.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cell4.setPhrase(new Paragraph("G.WT(Kg)", headFont));
        table.addCell(cell4);
        PdfPCell cell5 = new PdfPCell(); //创建单元格
        cell5.setMinimumHeight(30F);//表格高度
        cell5.setUseAscender(true); //开启单元格内文字位置设计
        cell5.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cell5.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cell5.setPhrase(new Paragraph("Package", headFont));
        table.addCell(cell5);
        /****每行的值****/
        List<String> invoiceList = new ArrayList<>();
        apply.getApplyInvoicesList().forEach(applyInvoices -> {
            invoiceList.add(isNotBlank(applyInvoices.getPn()) ? applyInvoices.getPn() : null); // 件号物料号
            invoiceList.add(isNotBlank(applyInvoices.getEnName()) ? applyInvoices.getEnName() : null); // 英文品名
            invoiceList.add(isNotEmpty(applyInvoices.getQty()) ? applyInvoices.getQty().toString() : null); // 数量
            invoiceList.add(isNotEmpty(applyInvoices.getWeight()) ? applyInvoices.getWeight().toString() : null); // 净重
            invoiceList.add(isNotEmpty(apply.getGrosswt()) ? apply.getGrosswt().toString() : null); // 毛重
            invoiceList.add(isNotEmpty(apply.getPacks()) ? apply.getPacks().toString() + " CASES" : null); // 总价
        });
        log.info("invoiceList==> " + invoiceList);
        int line = 0;
        for (int i = 0; i < invoiceList.size() / 6; i++) {
            for (int j = 0; j < 6; j++) {
                PdfPCell cell_ = new PdfPCell(); //创建单元格
                cell_.setMinimumHeight(30F);//表格高度
                cell_.setUseAscender(true);
                cell_.setHorizontalAlignment(Element.ALIGN_CENTER); //水平居中
                cell_.setVerticalAlignment(Element.ALIGN_MIDDLE); //垂直居中
                if (i == 0) {
                    if (j == 4 || j == 5) {
                        cell_.setRowspan(apply.getApplyInvoicesList().size());
                        String value = invoiceList.get(line);
                        cell_.setPhrase(new Paragraph(value, childFont));
                        table.addCell(cell_);
                    }
                }
                if (j != 4 && j != 5) {
                    cell_.setPhrase(new Paragraph(invoiceList.get(line), childFont));
                    table.addCell(cell_);
                }
                line++;
            }
        }
        // 处理最后一行
        BigDecimal totalQty = new BigDecimal("0");
        BigDecimal totalWeight = new BigDecimal("0");
        for (ApplyInvoices a : apply.getApplyInvoicesList()) {
            totalQty = totalQty.add(isNotEmpty(a.getQty()) ? a.getQty() : new BigDecimal("0"));
            totalWeight = totalWeight.add(isNotEmpty(a.getWeight()) ? a.getWeight() : new BigDecimal("0"));
        }
        PdfPCell cellLast = new PdfPCell(); //创建单元格
        cellLast.setMinimumHeight(30F);//表格高度
        cellLast.setColspan(2);
        cellLast.setUseAscender(true); //开启单元格内文字位置设计
        cellLast.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cellLast.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cellLast.setPhrase(new Paragraph("TOTAL", headFont));
        table.addCell(cellLast);
        PdfPCell cellLast1 = new PdfPCell(); //创建单元格
        cellLast1.setMinimumHeight(30F);//表格高度
        cellLast1.setUseAscender(true); //开启单元格内文字位置设计
        cellLast1.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cellLast1.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cellLast1.setPhrase(new Paragraph(totalQty.toString(), headFont));
        table.addCell(cellLast1);
        PdfPCell cellLast2 = new PdfPCell(); //创建单元格
        cellLast2.setMinimumHeight(30F);//表格高度
        cellLast2.setUseAscender(true); //开启单元格内文字位置设计
        cellLast2.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cellLast2.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cellLast2.setPhrase(new Paragraph(totalWeight.toString(), headFont));
        table.addCell(cellLast2);
        PdfPCell cellLast3 = new PdfPCell(); //创建单元格
        cellLast3.setMinimumHeight(30F);//表格高度
        cellLast3.setUseAscender(true); //开启单元格内文字位置设计
        cellLast3.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cellLast3.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cellLast3.setPhrase(new Paragraph(isNotEmpty(apply.getGrosswt()) ? apply.getGrosswt().toString() : "", headFont));
        table.addCell(cellLast3);
        PdfPCell cellLast4 = new PdfPCell(); //创建单元格
        cellLast4.setMinimumHeight(30F);//表格高度
        cellLast4.setUseAscender(true); //开启单元格内文字位置设计
        cellLast4.setHorizontalAlignment(Element.ALIGN_CENTER); //设置单元格的水平居中
        cellLast4.setVerticalAlignment(Element.ALIGN_MIDDLE); //设置单元格的垂直居中
        cellLast4.setPhrase(new Paragraph(isNotEmpty(apply.getPacks()) ? apply.getPacks().toString() + " CASES" : null, headFont));
        table.addCell(cellLast4);
    }
三、测试
1. 导出的发票如下:

在这里插入图片描述

2. 导出的箱单如下:

在这里插入图片描述
总的来说用itextpdf导出PDF还是很简单的,需要其他功能可以自己研究。

以上!

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
实现PDF预览可以使用以下步骤: 1. 后端使用SpringBoot框架,引入pdfbox库(可以使用Maven等工具进行管理),用于读取PDF文件内容。 2. 前端使用Vue框架,引入pdf.js库,用于在网页中展示PDF文件。 3. 后端提供一个接口,用于获取PDF文件内容,并返回给前端。 4. 前端通过Ajax请求后端接口,获取PDF文件内容,并使用pdf.js库在网页中展示PDF文件。 具体实现步骤如下: 后端实现: 1. 在pom.xml文件中添加pdfbox依赖: ```xml <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.17</version> </dependency> ``` 2. 定义一个Controller,提供一个接口用于获取PDF文件内容: ```java @RestController @RequestMapping("/api/pdf") public class PdfController { @GetMapping("/{filename}") public String getPdfContent(@PathVariable String filename) throws IOException { File file = new File("path/to/pdf/" + filename); PDDocument document = PDDocument.load(file); PDFTextStripper stripper = new PDFTextStripper(); String content = stripper.getText(document); document.close(); return content; } } ``` 3. 启动SpringBoot应用,访问`http://localhost:8080/api/pdf/test.pdf`即可获取test.pdf文件的内容。 前端实现: 1. 在Vue组件中引入pdf.js库: ```html <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.3.200/pdf.min.js"></script> ``` 2. 在Vue组件中定义一个方法,用于获取PDF文件内容并展示: ```javascript methods: { loadPdf(filename) { axios.get(`/api/pdf/${filename}`) .then(response => { const data = response.data; const pdfDoc = pdfjsLib.getDocument({data: data}); pdfDoc.promise.then(pdf => { for (let i = 1; i <= pdf.numPages; i++) { pdf.getPage(i).then(page => { const canvas = document.createElement('canvas'); const scale = 1.5; const viewport = page.getViewport({scale: scale}); const context = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; const renderContext = { canvasContext: context, viewport: viewport }; page.render(renderContext).promise.then(() => { const img = canvas.toDataURL(); // 将img添加到页面中展示 }); }); } }); }) .catch(error => { console.error(error); }); } } ``` 3. 调用loadPdf方法,传入PDF文件名即可展示PDF文件。 以上就是使用SpringBootVue实现PDF预览的步骤,可以根据实际需求进行调整和优化。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值