首页 > 其他分享 >后端笔记 - iText5处理pdf

后端笔记 - iText5处理pdf

时间:2023-09-27 14:37:22浏览次数:33  
标签:int 笔记 iText5 addCell textBoldFont signAbsY pdf PDFUtil table

1.引入依赖

        <!-- 生成PDF的工具包 -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.12</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

2.生成pdf

public static void createBillPdf(BillInfo billInfo, BankAccount bankAccount, String filePath, Customer customer, User saler) {
        df.setRoundingMode(RoundingMode.DOWN);// 舍去分的后一位

        Document document = new Document(PageSize.A4, 36, 36, 72, 36);
        File file = null;
        try {
            BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            // 设置字体样式
            Font textFont = new Font(bfChinese, 10, Font.NORMAL);// 正常
            Font textBoldFont = new Font(bfChinese, 10, Font.BOLD);// 加粗
            Font boldFont = new Font(bfChinese, 18, Font.BOLD); // 加粗

            file = File.createTempFile("temp", ".pdf");
            if (!file.exists()) {
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
            }
            PdfWriter.getInstance(document, new FileOutputStream(file));
            document.open();
            Paragraph p0 = new Paragraph();

            Paragraph p1 = new Paragraph("", textFont);
            p1.setLeading(40);
            p0.add(p1);

            // 创建table对象
            PdfPTable table = new PdfPTable(4);
            table.setSpacingBefore(10);
            table.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.setTotalWidth(new float[] { 110, 110, 110, 110 }); // 设置列宽
            table.setLockedWidth(true); // 锁定列宽

            PdfPCell cell = new PdfPCell();
            // 添加表格内容
            cell = PDFUtil.mergeCol(Constants.EXCEL_BILL_TITLE, boldFont, 4);
            cell.setFixedHeight(60);
            cell.setVerticalAlignment(Element.ALIGN_CENTER);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            cell.setPaddingTop(25);
            table.addCell(cell);

            table.addCell(PDFUtil.mergeCol("客户:" + billInfo.getCompanyName(), textBoldFont, 4));

            // 客户联系人
            // 联系人
            table.addCell(PDFUtil.mergeCol("客户联系人:" + (StringUtils.isBlank(billInfo.getContactsName()) ? "" : billInfo.getContactsName()), textBoldFont, 4));

            // 联系人部门
            table.addCell(PDFUtil.getPDFCell("部门", textBoldFont));
            if (StringUtil.isNotBlank(customer.getContactDept())) {
                table.addCell(PDFUtil.getPDFCell(customer.getContactDept(), textBoldFont));
            } else {
                table.addCell(PDFUtil.getPDFCell("", textBoldFont));
            }
            // 联系人职务
            table.addCell(PDFUtil.getPDFCell("联系人职务", textBoldFont));
            if (StringUtil.isNotBlank(customer.getContactPosition())) {
                table.addCell(PDFUtil.getPDFCell(customer.getContactPosition(), textBoldFont));
            } else {
                table.addCell(PDFUtil.getPDFCell("", textBoldFont));
            }
            // 联系人座机
            table.addCell(PDFUtil.getPDFCell("座机", textBoldFont));
            if (StringUtil.isNotBlank(customer.getContactTelephone())) {
                table.addCell(PDFUtil.getPDFCell(customer.getContactTelephone(), textBoldFont));
            } else {
                table.addCell(PDFUtil.getPDFCell("", textBoldFont));
            }
            // 联系人手机
            table.addCell(PDFUtil.getPDFCell("联系人手机", textBoldFont));
            if (StringUtil.isNotBlank(billInfo.getPhone())) {
                table.addCell(PDFUtil.getPDFCell(billInfo.getPhone(), textBoldFont));
            } else {
                table.addCell(PDFUtil.getPDFCell("", textBoldFont));
            }

            table.addCell(PDFUtil.mergeCol("账单编号:" + billInfo.getBillNumber(), textBoldFont, 4));

            String billStartTime = sdf.format(billInfo.getBillDate());
            String billEndTime = sdf.format(DateUtil.getThisMonthFinal(billInfo.getBillDate()));
            table.addCell(PDFUtil.mergeCol("计费周期:" + billStartTime + "至" + billEndTime, textBoldFont, 4));

            table.addCell(PDFUtil.getPDFCell("账号", textBoldFont));
            table.addCell(PDFUtil.getPDFCell("单价(元/条)", textBoldFont));
            table.addCell(PDFUtil.getPDFCell("计费条数(条)", textBoldFont));
            table.addCell(PDFUtil.getPDFCell("金额(元)", textBoldFont));

            if (!CollectionUtils.isEmpty(billInfo.getAccountInfos())) {
                for (DetailInfo detail : billInfo.getAccountInfos()) {
                    table.addCell(PDFUtil.getPDFCell(detail.getAccountName(), textFont));
                    table.addCell(PDFUtil.getPDFCellRight(dff.format(detail.getUnitPrice()), textFont));
                    table.addCell(PDFUtil.getPDFCellRight(dft.format(detail.getFeeCount()), textFont));
                    table.addCell(PDFUtil.getPDFCellRight("¥" + df.format(detail.getFee()), textFont));
                }
            }

            table.addCell(PDFUtil.getPDFCell("实际计费", textBoldFont));
            table.addCell(PDFUtil.getPDFCellRight(dff.format(billInfo.getRealFeeInfo().getUnitPrice()), textFont));
            table.addCell(PDFUtil.getPDFCellRight(dft.format(billInfo.getRealFeeInfo().getFeeCount()), textFont));
            table.addCell(PDFUtil.getPDFCellRight("¥" + df.format(billInfo.getRealFeeInfo().getFee().setScale(2, BigDecimal.ROUND_UP)), textFont));

            table.addCell(PDFUtil.getPDFCell("本期应付(大写金额)", textBoldFont));
            table.addCell(PDFUtil.mergeCol(Money2ChineseUtil.convert(billInfo.getRealFeeInfo().getFee()), textBoldFont, 2));
            table.addCell(PDFUtil.getPDFCellRight("¥" + df.format(billInfo.getRealFeeInfo().getFee().setScale(2, BigDecimal.ROUND_UP)), textFont));

            table.addCell(PDFUtil.mergeCol(
                    "出账日期:" + sdf.format(billInfo.getCreateDate()) + "                                   最后付款日期:" + sdf.format(billInfo.getFinalPayDate()),
                    textBoldFont, 4));

            table.addCell(PDFUtil.mergeCol("收款银行账号信息", textBoldFont, 4));

            table.addCell(PDFUtil.mergeCol(bankAccount.getAccountName(), textFont, 4));
            table.addCell(PDFUtil.mergeCol(bankAccount.getAccountBank(), textFont, 4));
            table.addCell(PDFUtil.mergeCol(bankAccount.getBankAccount(), textFont, 4));
            table.addCell(PDFUtil.mergeCol(bankAccount.getCompanyAddress(), textFont, 4));

            table.addCell(PDFUtil.mergeCol("销售经理:" + billInfo.getSaleName(), textBoldFont, 4));
            table.addCell(
                    PDFUtil.mergeCol("经理座机:" + (saler == null || StringUtils.isBlank(saler.getContactPhone()) ? "" : saler.getContactPhone()), textBoldFont, 4));
            table.addCell(PDFUtil.mergeCol("经理手机:" + billInfo.getSalePhone(), textBoldFont, 4));

            table.addCell(PDFUtil.mergeCol("温馨提示:本单据为您电子对账单,请妥善保管。", textBoldFont, 4, 0));

            p0.add(table);
            document.add(p0);

            // 关闭文档
            document.close();

            // 文件转写
            int billTableH = (int) table.getTotalHeight();
            File signFile = file;
            if (BillFlow.BILL_SIGNATURE_OPT_NEED) {
                int baseAbsY = (billTableH % 710 == 0) ? 0 : (710 - billTableH % 710);
                int signAbsX = 320, signAbsY = baseAbsY + 5 * 25;
                int pageNumber = (billTableH % 710 == 0) ? billTableH / 710 : billTableH / 710 + 1;
                if (signAbsY > 710) {
                    pageNumber --;
                    signAbsY -= 710;
                }
                signAbsY = IText5PdfUtil.calAndMovedY(signAbsY, 710, 50);
                URL signImg = loadMetaData(IText5Pdf4MergeUtil.class.getClassLoader(), Bill.BILL_SIGNATURE_OPT_IMG);
                signFile = IText5PdfUtil.addPdfImgSignature(file, null, signImg, signAbsX, signAbsY, new int[]{pageNumber}, 0.5f);
            }

            // 获取水印文件路径
            URL logoImageURL = loadMetaData(IText5PdfUtil.class.getClassLoader(), "static/common/imgs/watermark.png");
            addPdfImgMark(signFile, filePath, logoImageURL);

        } catch (Exception e) {
            logger.error("账单生成异常:", e);
        } finally {
            if (file != null) {
                file.delete();
            }
        }
    }

3.写入印章

    public static URL loadMetaData(ClassLoader classLoader, String path) {
        try {
            Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
            UrlResource resource = new UrlResource(urls.nextElement());
            return resource.getURL();
        } catch (Exception e) {
            logger.error("", e);
        }
        return null;
    }

/**
     * 方法描述:为PDF文档增加图片签章(这种写法图片只能位于文字之下,并且不能跨页)
     * @param doc 文档源,原文件即需要增加水印的文件
     * @param imgPath 签章图片源路径
     * @param coordinate 需要加图片的坐标
     * @param scale 缩放百分比数值(如:35.5%, scale=35.5f)
     * @date 2023-09-20 15:50:39
     */
    public static void addPdfImgSignature(Document doc, String imgPath, int[] coordinate, float scale) {
        try {
            URL signatureURL = loadMetaData(IText5Pdf4MergeUtil.class.getClassLoader(), imgPath);
            Assert.notNull(signatureURL, "签章文件[" + imgPath + "]空");
            Image img = Image.getInstance(signatureURL);
            img.scalePercent(scale);
            img.setAbsolutePosition(coordinate[0], coordinate[1]);
            doc.add(img);
        } catch (Exception e) {
            logger.error("签章:{} 添加失败. ", imgPath, e);
        }
    }

    /**
     * 方法描述:为PDF文档增加图片签章
     * @param inFile 待处理文件
     * @param tagSignFile 价签之后的目标文件
     * @param imgUrl URL
     * @param x 签章x坐标 (坐标最下开始,图标也是以坐标从下往上渲染)
     * @param y 签章x坐标
     * @param pageNum 目标页码
     * @param scale 签章图缩放比例
     * @return <File> | 输出加签之后的
     */
    public static File addPdfImgSignature(File inFile, File tagSignFile, URL imgUrl, int x, int y, int[] pageNum, float scale) {
        File signFile = tagSignFile;
        try {
            if (Objects.isNull(tagSignFile)) {
                signFile = File.createTempFile("tmp", ".pdf");
                if (!signFile.exists()) {
                    if (!signFile.getParentFile().exists()) {
                        signFile.getParentFile().mkdirs();
                    }
                }
            }
            PdfReader reader = new PdfReader(inFile.getPath(), "PDF".getBytes());
            PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(signFile));
            stamp.setRotateContents(false);

            Image img = Image.getInstance(imgUrl);
            img.setAbsolutePosition(x, y);
            img.scaleAbsoluteWidth(img.getWidth() * scale);
            img.scaleAbsoluteHeight(img.getHeight() * scale);
            for (int pn : pageNum) {
                PdfContentByte over = stamp.getOverContent(pn);
                over.addImage(img);
            }
            stamp.close();
            reader.close();
        } catch (Exception e) {
            logger.error("PDF签名失败", e);
        }
        return Objects.isNull(signFile) ? inFile : signFile;
    }

/**
* 方法描述: 计算渲染位置需要平移后的位置
* <p>
* 目标点在最后一页中的位置高度H:
* <br/>&nbsp; 1.当 pageH - imgW > signAbsY > imgW, 则无需移动;
* <br/>&nbsp; 2.当 signAbsY < imgW, 则向上移,至大于 imgW;
* <br/>&nbsp; 3.当 signAbsY <= pageH & signAbsY > pageH - imgW, 向下移动,至小于 pageH - imgW;
* <br/>&nbsp; 4.当 signAbsY > pageH, 对 signAbsY % pageH 进行递归判断, 整数页的高度 + calAndMovedY(signAbsY % pageH);
* </p>
* @param signAbsY 目标渲染点的高度位置
* @param pageH 每个页面内容的总高度
* @param imgW 图片宽度
* @return {@link int} 目标渲染位置需要平移后的位置
* @date 2023-09-21 14:31:57
*/
public static int calAndMovedY(int signAbsY, int pageH, int imgW) {
int minTop = pageH - imgW;
if (signAbsY > imgW && signAbsY < minTop) {
return signAbsY;
}
if (signAbsY < imgW) {
// 向上移,至大于 imgW
signAbsY = imgW + 5;
}
if (signAbsY > pageH - imgW) {
// 向下移动,至小于 pageH - imgW
signAbsY = pageH - imgW;
}

/*

// 不在最后1页时会渲染不上,这种情况暂时不处理
if (signAbsY <= pageH && signAbsY > pageH - imgW) {
// 向下移动,至小于 pageH - imgW
signAbsY = pageH - imgW;
}
if (signAbsY > pageH) {
// 对 signAbsY % pageH 进行递归判断, 整数页的高度 + calAndMovedY(signAbsY % pageH)
int subY = signAbsY % pageH;
signAbsY -= subY;
signAbsY += calAndMovedY(subY, pageH, imgW);
}

*/

return signAbsY;
}
 

4.写入水印

/**
     * 方法描述:为PDF文档增加水印, 不改变原文件情况下增加水印
     *              如果原文件与输出文件地址相同,则原文件将增加水印
     * @param inFile 文档源,原文件即需要增加水印的文件
     * @param outPdfFile 带水印的文档保存路径
     * @return 是否成功
     * @date 2022-04-02 15:50:39
     */
    protected static boolean addPdfImgMark(File inFile, String outPdfFile, URL logoImageURL) {
        boolean result = false;
        PdfStamper stamp = null;
        PdfReader reader = null;
        File outFile = null;
        try {
            //若为同一文件
            if (Objects.equals(inFile.getPath(), outPdfFile)) {
                outFile = inFile;
                inFile = new File(outFile.getParentFile(), System.currentTimeMillis() + ".pdf");
                FileUtils.copyFile(outFile, inFile);
            } else {
                outFile = new File(outPdfFile);
            }
            if (!outFile.exists()) {
                if (!outFile.getParentFile().exists()) {
                    outFile.getParentFile().mkdirs();
                }
            }
            //读取流,会与下面写入流相冲突
            reader = new PdfReader(inFile.getPath(), "PDF".getBytes());
            stamp = new PdfStamper(reader, new FileOutputStream(outFile));
//            // 加密码,设置权限,仅允许复制、打印
//            String pwd = UUID.randomUUID().toString().replace("-", "");
//            int permissions = PdfWriter.ALLOW_COPY | PdfWriter.ALLOW_PRINTING;
//            stamp.setEncryption(null, pwd.getBytes(), permissions, false);

            PdfGState gs1 = new PdfGState();
            gs1.setFillOpacity(0.2f);// 透明度设置

            int imgWidth = 20;
            int imgHeight = 480;

            Image img = Image.getInstance(logoImageURL);// 插入图片水印

            img.setAbsolutePosition(imgWidth, imgHeight); // 坐标
            img.setRotation(-20);// 旋转 弧度
            img.setRotationDegrees(-10);// 旋转 角度
            // img.scaleAbsolute(200,100);//自定义大小
            img.scalePercent(21);// 依照比例缩放

            int pageSize = reader.getNumberOfPages();// 原pdf文件的总页数
            PdfContentByte under;
            for (int i = 1; i <= pageSize; i++) {
                under = stamp.getUnderContent(i);// 水印在之前文本下
                under.setGState(gs1);// 图片水印 透明度
                under.addImage(img);// 图片水印
            }
            int imgWidth2 = 320;
            int imgHeight2 = 300;
            Image img2 = Image.getInstance(logoImageURL);// 插入图片水印

            img2.setAbsolutePosition(imgWidth2, imgHeight2); // 坐标
            img2.setRotation(-20);// 旋转 弧度
            img2.setRotationDegrees(-10);// 旋转 角度
            img2.scalePercent(21);// 依照比例缩放

            for (int i = 1; i <= pageSize; i++) {
                under = stamp.getUnderContent(i);// 水印在之前文本下
                under.setGState(gs1);// 图片水印 透明度
                under.addImage(img2);// 图片水印

            }
            result = true;
        } catch (Exception e) {
            logger.error("", e);
        } finally {
            try {
                inFile.deleteOnExit();
                if (stamp != null) {
                    stamp.close();
                }
                if (reader != null) {
                    reader.close();
                }
            } catch (DocumentException | IOException e) {
                e.printStackTrace();
            }

        }
        return result;
    }

 

标签:int,笔记,iText5,addCell,textBoldFont,signAbsY,pdf,PDFUtil,table
From: https://www.cnblogs.com/coder-ydq/p/17732621.html

相关文章

  • Android上层WatchDog学习笔记_2
    一、简述1.了解WatchDog的原理,可以更好的理解系统服务的运行机制。二、WatchDog实现1.代码实现位置//frameworks/base/services/core/java/com/android/server/Watchdog.javapublicclassWatchdogextendsThread{...}可见Watchdog是一个线程。2.WatchDo......
  • CPP面向对象笔记
    基本属性即在类中包含的一系列变量方法即在类中定义的一系列函数Public,PrivateandProtected在没有继承的情况下,private与protected效果相同即都无法在类外直接访问调用实在想要访问,加个函数就行public则可以随意访问调用static仅与类的整体全局有关不受具体哪......
  • 动态规划——矩阵优化DP 学习笔记
    动态规划——矩阵优化DP学习笔记前置知识:矩阵、矩阵乘法。矩阵乘法优化线性递推斐波那契数列在斐波那契数列当中,\(f_1=f_2=1\),\(f_i=f_{i-1}+f_{i-2}\),求\(f_n\)。而分析式子可以知道,求\(f_k\)仅与\(f_{k-1}\)和\(f_{k-2}\)有关;所以我们设矩阵\(F_......
  • Vue2.0 浅学笔记
    Vue是框架,也是生态。1.VueAPI风格选项式(Vue2)组合式(Vue3)2.入门node.js版本大于153.创建项目创建项目npminitvue@latest开发环境VScode+Volar4.基本语法1.文本插值仅能使用单一表达式使用JavaScript表达式每个绑定仅支持单一表达式,也就是一段能够被求值的J......
  • 500_想在iPad上学习?这个PDF电子书搜索引擎实在太好用
    这是一篇原发布于2020-02-2909:50:00得益小站的文章,备份在此处。前段时间,各家出版社纷纷“用知识抗击疫情”,开放了自家的图书资源来倡导读者在家学习。轶哥也下载了许多电子书。不得不说原版的电子书质量就是高,这些电子书可以完美的标注,复制;相较于影印版PDF体积更小,阅读体验也......
  • EMQX学习笔记:命令行工具
    本文更新于2023-02-28,使用EMQX4.4.3。目录emqxemqx_ctlemqx官方文档:https://www.emqx.io/docs/zh/v4.4/getting-started/command-line.htmlemqxconsole:控制台模式。emqxrestart:重启EMQX。emqxstart:启动EMQX。emqx_ctl官方文档:https://www.emqx.io/docs/zh/v4.4/adva......
  • MMU复习--Apple的学习笔记
    一,前言以前看过MMU,因为这是单片机OS中没有的,当时我记得理解的不是很清晰,包括MMU中哪部分是硬件的,哪部分是软件的都没有太搞清楚。由于看了一个自己写linux操作系统的视频,里面有介绍MMU,且演示了虚拟地址和物理地址的转换,此时我才深刻的理解了,所以在看qemu源码的内存管理前,我先复习......
  • MMU复习--Apple的学习笔记
    一,前言以前看过MMU,因为这是单片机OS中没有的,当时我记得理解的不是很清晰,包括MMU中哪部分是硬件的,哪部分是软件的都没有太搞清楚。由于看了一个自己写linux操作系统的视频,里面有介绍MMU,且演示了虚拟地址和物理地址的转换,此时我才深刻的理解了,所以在看qemu源码的内存管理前,我先复习下......
  • 《Java编程思想第四版》学习笔记31--关于Externalizable
    //:Blip3.java//Reconstructinganexternalizableobjectimportjava.io.*;importjava.util.*;classBlip3implementsExternalizable{inti;Strings;//NoinitializationpublicBlip3(){System.out.println("Blip3Constructor");//s,inoti......
  • JMockit学习笔记
    1基本概念1.1常用注解@Mocked:被修饰的对象将会被Mock,对应的类和实例都会受影响(同一个测试用例中)@Injectable:仅Mock被修饰的对象@Capturing:可以mock接口以及其所有的实现类@Mock:MockUp模式中,指定被Fake的方法1.2常用的类Expectations:期望,指定的方法必须被调用StrictExpectati......