Java把列表数据导出为PDF文件,同时加上PDF水印

news2024/11/19 11:31:10

一、实现效果

在这里插入图片描述

二、遇到的问题

  1. 实现导出PDF主体代码参考:Java纯代码实现导出PDF功能,下图是原作者实现的效果
    在这里插入图片描述
  2. 导出报错Font 'STSong-Light' with 'UniGB-UCS2-H' is not recognized.。参考:itext 生成 PDF(五) 使用外部字体

网上都是说jar包的版本不对,导致的字体兼容性问题。换了jar包版本发现没效果,后来索性直接把字体下载到本地直接引入。

  1. jar包发布到服务器上导出PDF的时候发生报错BOOT-INF/classes!/fonts/SimSun.ttf not exists

可以看到字体文件在jar目录下面是有的,但是发现classes后面多了个叹号。这是引入外部字体方式不对,后改用问题2参考文章的第三种写法就没问题了。

  1. 添加水印参考:itextpdf5.5.13给pdf添加图片水印、添加文字水印(平铺)、添加文字水印(单个)、添加页眉、页脚、页眉事件、添加图片

三、测试数据展示

list:子节点数据
0 = {BasBudgetDetailVo@16046} "BasBudgetDetailVo(budgetId=2064535550, functionId=231231232, budgetQuantity=3, totalPrice=2664.00, functionName=功能1, functionDescription=功能1描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=1)"
1 = {BasBudgetDetailVo@16047} "BasBudgetDetailVo(budgetId=2039369726, functionId=231236478, budgetQuantity=1, totalPrice=888.00, functionName=功能1, functionDescription=功能1描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=1)"
2 = {BasBudgetDetailVo@16048} "BasBudgetDetailVo(budgetId=2039369725, functionId=231236473, budgetQuantity=1, totalPrice=888.00, functionName=功能2, functionDescription=功能2描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=2)"
3 = {BasBudgetDetailVo@16049} "BasBudgetDetailVo(budgetId=2056146943, functionId=231231241, budgetQuantity=1, totalPrice=888.00, functionName=功能2, functionDescription=功能2描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=2)"
4 = {BasBudgetDetailVo@16050} "BasBudgetDetailVo(budgetId=2047758334, functionId=231236487, budgetQuantity=1, totalPrice=888.00, functionName=功能3, functionDescription=功能3描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=3)"
5 = {BasBudgetDetailVo@16051} "BasBudgetDetailVo(budgetId=2039369724, functionId=231231245, budgetQuantity=1, totalPrice=888.00, functionName=功能3, functionDescription=功能3描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=3)"
6 = {BasBudgetDetailVo@16052} "BasBudgetDetailVo(budgetId=2047758333, functionId=231231597, budgetQuantity=1, totalPrice=888.00, functionName=功能4, functionDescription=功能4描述, functionUnit=套, functionPrice=888.00, parentId=231234512, functionSort=4)"
7 = {BasBudgetDetailVo@16053} "BasBudgetDetailVo(budgetId=2030981118, functionId=231233154, budgetQuantity=1, totalPrice=888.00, functionName=功能4, functionDescription=功能4描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=4)"
8 = {BasBudgetDetailVo@16054} "BasBudgetDetailVo(budgetId=2030981117, functionId=231234596, budgetQuantity=1, totalPrice=888.00, functionName=功能5, functionDescription=功能5描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=5)"
9 = {BasBudgetDetailVo@16055} "BasBudgetDetailVo(budgetId=2030981116, functionId=231235487, budgetQuantity=1, totalPrice=888.00, functionName=功能6, functionDescription=功能6描述, functionUnit=套, functionPrice=888.00, parentId=231234879, functionSort=6)"

functionInfoList:根节点数据
0 = {BasFunctionInfo@16090} "BasFunctionInfo(functionId=231234512, functionName=模块1, functionDescription=, functionUnit=0, functionPrice=0.00, createName=管理员, createBy=admin, createTime=Wed Jan 24 16:56:35 CST 2024, updateName=管理员, updateBy=admin, updateTime=Wed Jan 24 16:56:38 CST 2024, functionQuantity=null, functionSort=1, parentId=null)"
1 = {BasFunctionInfo@16091} "BasFunctionInfo(functionId=231234879, functionName=模块2, functionDescription=, functionUnit=0, functionPrice=0.00, createName=管理员, createBy=admin, createTime=Wed Jan 24 16:56:35 CST 2024, updateName=管理员, updateBy=admin, updateTime=Wed Jan 24 16:56:38 CST 2024, functionQuantity=null, functionSort=2, parentId=null)"

matchList:当前节点的子节点数据

四、jar包引入

<!--导出pdf所需包-->
<dependency>
	<groupId>com.itextpdf</groupId>
	<artifactId>itextpdf</artifactId>
	<version>5.5.10</version>
</dependency>
<dependency>
	<groupId>com.itextpdf</groupId>
	<artifactId>itext-asian</artifactId>
	<version>5.2.0</version>
</dependency>
</dependencies>

五、外部字体引入

字体文件资源自己百度,直接搜SimSun.ttf字体下载不难找
在这里插入图片描述

六、代码实现

private final ResourceLoader resourceLoader;

public BasBudgetDetailServiceImpl(ResourceLoader resourceLoader) {
    this.resourceLoader = resourceLoader;
}

/**
 * 导出pdf
 * 
 * @param response
 * @throws Exception
 */
@Override
public void downloadPdf(HttpServletResponse response) throws Exception {
	// 业务数据,根据需求查询获取
    // 子节点数据
    List<BasBudgetDetailVo> list;
    // 根子节点数据
    List<BasFunctionInfo> functionInfoList;

    // 定义全局的字体静态变量
    Font content = null;
    Resource resource = resourceLoader.getResource("classpath:/fonts/SimSun.ttf");
    InputStream inputStream = resource.getInputStream();
    BaseFont bfChinese = null;
    try {
        // 字体
        bfChinese = BaseFont.createFont("SimSun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, IOUtils.toByteArray(inputStream), null);
        content = new Font(bfChinese, 10, Font.NORMAL);
    } catch (Exception e) {
        e.printStackTrace();
    }
    BaseFont bf = null;
    Font font = null;
    try {
        //创建字体
        bf = BaseFont.createFont("SimSun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, IOUtils.toByteArray(inputStream), null);
        //使用字体并给出颜色
        font = new Font(bf, 20, Font.BOLD, BaseColor.BLACK);
    } catch (Exception e) {
        e.printStackTrace();
    }
    Document document = new Document(new RectangleReadOnly(842F, 595F));
    try {
        PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
        //打开生成的pdf文件
        document.open();
        //设置标题
        Paragraph paragraph = new Paragraph("这是标题文档标题", font);
        paragraph.setAlignment(1);
        //引用字体
        document.add(paragraph);

        // 总额
        BigDecimal detailTotal = BigDecimal.valueOf(0);
        for (BasFunctionInfo functionInfo : functionInfoList) {
            // 匹配明细
            List<BasBudgetDetailVo> matchList = list.stream().filter(item ->
                            String.valueOf(item.getParentId()).equals(String.valueOf(functionInfo.getFunctionId())))
                    .collect(Collectors.toList());

            // 设置表格的列宽和列数
            float[] widths = {10f, 35f, 70f, 10f, 10f, 20f, 20f};
            PdfPTable table = new PdfPTable(widths);
            table.setSpacingBefore(20f);
            // 设置表格宽度为100%
            table.setWidthPercentage(100.0F);
            table.setHeaderRows(1);
            table.getDefaultCell().setHorizontalAlignment(1);
            //列表-表头
            String[] titleList = new String[]{"序号", "功能名称", "功能描述", "数量", "单位", "单价(元)", "总价(元)"};
            addTableTitle(table, content, titleList);
            // 模块总额
            BigDecimal modelTotal = BigDecimal.valueOf(0);
            //列表数据
            if (matchList.size() > 0) {
                Integer index = 1;
                for (BasBudgetDetailVo item : matchList) {
                    PdfPCell cell1 = new PdfPCell(new Paragraph(String.valueOf(index), content));
                    PdfPCell cell2 = new PdfPCell(new Paragraph(item.getFunctionName(), content));
                    PdfPCell cell3 = new PdfPCell(new Paragraph(item.getFunctionDescription(), content));
                    PdfPCell cell4 = new PdfPCell(new Paragraph(String.valueOf(item.getBudgetQuantity()), content));
                    PdfPCell cell5 = new PdfPCell(new Paragraph(item.getFunctionUnit(), content));
                    PdfPCell cell6 = new PdfPCell(new Paragraph(String.valueOf(item.getFunctionPrice()), content));
                    BigDecimal totalPrice = item.getFunctionPrice().multiply(BigDecimal.valueOf(item.getBudgetQuantity()));
                    PdfPCell cell7 = new PdfPCell(new Paragraph(String.valueOf(totalPrice), content));
                    //单元格对齐方式
                    cell1.setFixedHeight(20);
                    cell1.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);

					// 文字长度大于15的时候,设置表格行间距,底边距离
                    if (item.getFunctionName().length() > 15) {
                        cell2.setLeading(0f, 1.5f);
                        cell2.setPaddingBottom(10);
                    }
                    cell2.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
					
					// 文字长度大于30的时候,设置表格行间距,底边距离
                    if (item.getFunctionDescription().length() > 30) {
                        cell3.setLeading(0f, 1.5f);
                        cell3.setPaddingBottom(10);
                    }
                    cell3.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);

                    cell4.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell4.setVerticalAlignment(Element.ALIGN_MIDDLE);

                    cell5.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell5.setVerticalAlignment(Element.ALIGN_MIDDLE);

                    cell6.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell6.setVerticalAlignment(Element.ALIGN_MIDDLE);

                    cell7.setHorizontalAlignment(Element.ALIGN_CENTER);
                    cell7.setVerticalAlignment(Element.ALIGN_MIDDLE);

                    table.addCell(cell1);
                    table.addCell(cell2);
                    table.addCell(cell3);
                    table.addCell(cell4);
                    table.addCell(cell5);
                    table.addCell(cell6);
                    table.addCell(cell7);

                    // 序号
                    index++;

                    modelTotal = modelTotal.add(totalPrice);
                }
                // 合计行
                PdfPCell cell1 = new PdfPCell(new Paragraph("合计", content));
                cell1.setFixedHeight(20);
                cell1.setHorizontalAlignment(Element.ALIGN_CENTER);
                cell1.setVerticalAlignment(Element.ALIGN_MIDDLE);
                // 空格
                PdfPCell cell2 = new PdfPCell(new Paragraph("", content));
                cell2.setFixedHeight(20);
                cell2.setHorizontalAlignment(Element.ALIGN_CENTER);
                cell2.setVerticalAlignment(Element.ALIGN_MIDDLE);
                // 数额
                PdfPCell cell3 = new PdfPCell(new Paragraph(String.valueOf(modelTotal), content));
                cell3.setFixedHeight(20);
                cell3.setHorizontalAlignment(Element.ALIGN_CENTER);
                cell3.setVerticalAlignment(Element.ALIGN_MIDDLE);
                table.addCell(cell1);
                table.addCell(cell2);
                table.addCell(cell2);
                table.addCell(cell2);
                table.addCell(cell2);
                table.addCell(cell2);
                table.addCell(cell3);

                detailTotal = detailTotal.add(modelTotal);
            }

            document.add(new Paragraph("\n"));
            document.add(new Paragraph("▋ " + functionInfo.getFunctionName(), content));
            document.add(table);
            document.add(new Paragraph("\n"));

            if (matchList.size() == 0) {
                document.add(new Paragraph("暂无数据", content));
            }
        }

        document.add(new Paragraph("\n"));
        document.add(new Paragraph("总计:" + detailTotal + "元", content));

        // 加水印
        PdfContentByte waterMar = pdfWriter.getDirectContentUnder();
        String text = "天天想辞职月月拿全勤";
        addTextFullWaterMark(waterMar, text, bfChinese);

        document.close();
    } catch (DocumentException e) {
        e.printStackTrace();
        log.error("导出pdf失败:{}", e);
    }
}

/**
 * 给表格添加表头
 *
 * @param table
 * @param content
 * @param titleList
 */
public void addTableTitle(PdfPTable table, Font content, String[] titleList) {
    PdfPCell cell = null;
    for (String title : titleList) {
        cell = new PdfPCell(new Paragraph(title, content));
        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        cell.setFixedHeight(20);
        cell.setNoWrap(false);
        table.addCell(cell);
    }
}

/**
 * 给pdf添加文字水印(平铺)
 *
 * @param waterMar
 * @param text     水印文本
 * @throws Exception
 */
public static void addTextFullWaterMark(PdfContentByte waterMar, String text, BaseFont bf) {
    waterMar.beginText();

    PdfGState gs = new PdfGState();
    // 设置填充字体不透明度为0.2f
    gs.setFillOpacity(0.2f);
    waterMar.setFontAndSize(bf, 20);
    // 设置透明度
    waterMar.setGState(gs);
    // 设置水印对齐方式 水印内容 X坐标 Y坐标 旋转角度
    for (int x = 0; x <= 900; x += 200) {
        for (int y = -50; y <= 800; y += 200) {
            waterMar.showTextAligned(Element.ALIGN_RIGHT, text, x, y, 35);
        }
    }

    // 设置水印颜色
    waterMar.setColorFill(BaseColor.GRAY);

    //结束设置
    waterMar.endText();
    waterMar.stroke();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1428286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java项目:基于SSM框架实现的西安旅游管理系统(ssm+B/S架构+源码+数据库+毕业论文)

一、项目简介 本项目是一套ssm811基于SSM框架实现的西安旅游管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&am…

Vue学习笔记14 --自定义hook函数/toRef/provide/inject等

9.自定义hook函数 什么是hook&#xff1f;—— 本质是一个函数&#xff0c;把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。 10.toRef 作用&#xff1a;创建一个 ref 对象&#xff0c;其…

T113-Pro的buildroot添加gdisk ( GPT disks )出现gptfdisk needs a toolchain w/ C++的解决方法

问题背景&#xff1a; 最近入手了百问网的全志T113-Pro&#xff0c;用Emmc启动发现一张32GB的SD卡在烧录了百问网镜像 100ask-t113-pro_sdcard.img 的系统后&#xff0c;仅有200多M的存储空间。第一时间上百问网论坛看是否有板友也出现类似情况&#xff0c;发现了一个帖子正是描…

Qt/C++音视频开发65-切换声卡/选择音频输出设备/播放到不同的声音设备/声卡下拉框

一、前言 近期收到一个用户需求&#xff0c;要求音视频组件能够切换声卡&#xff0c;首先要在vlc上实现&#xff0c;于是马不停蹄的研究起来&#xff0c;马上查阅对应vlc有没有自带的api接口&#xff0c;查看接口前&#xff0c;先打开vlc播放器&#xff0c;看下能不能切换&…

算法学习——华为机考题库1(HJ1 - HJ10)

算法学习——华为机考题库1&#xff08;HJ1 - HJ10&#xff09; HJ1 字符串最后一个单词的长度 描述 计算字符串最后一个单词的长度&#xff0c;单词以空格隔开&#xff0c;字符串长度小于5000。&#xff08;注&#xff1a;字符串末尾不以空格为结尾&#xff09; 输入描述&…

MySQL原理(三)锁定机制(2)表锁行锁与页锁

前面提到&#xff0c;mysql锁按照操作颗粒分类&#xff0c;一般认为有表级锁、行级锁、页面锁三种。其实还有一种特殊的全局锁。 锁场景问题全局锁全库逻辑备份加了全局锁之后&#xff0c;整个数据库都是【只读状态】&#xff0c;如果数据库里有很多数据&#xff0c;备份就会花…

撰写出色的时事政治新闻资讯稿:窍门和技巧

撰写出色的时事政治新闻资讯稿&#xff1a;窍门和技巧 文章大纲写新闻/资讯&#xff08;结构部分&#xff09;较为复杂的标题&#xff08;额外扩展&#xff09;相关案例去除引题去除引题和副题注意事项讲一下什么叫导语。叙述式结论式描写式提问式摘要式 主体一要新二要“小”三…

C++多线程3

生产者消费者模型 OS经典问题&#xff0c;生产者消费者模型,empty和full还有mutex对应到C上如何处理看代码即可 #include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <queue> using namespace st…

ElementUI Form:Select 选择器

ElementUI安装与使用指南 Select 选择器 点击下载learnelementuispringboot项目源码 效果图 el-select.vue&#xff08;Select选择器&#xff09;页面效果图 项目里el-select.vue代码 <script> export default {name: el_select,data() {return {options: [{value…

模糊神经网络控制器(MATLAB)

模糊神经网络控制器(Fuzzy Neural Network Controller)是将模糊控制和神经网络相结合的一类控制器。它综合了两者的优点,主要包括以下特点: 知识表达能力强。模糊系统的语言规则和神经网络的学习能力相结合,可以表示复杂的非线性映射关系。 自适应能力强。神经网络提供了在线学…

MySQL进阶之锁(全局锁以及备份报错解决)

锁 全局锁 全局锁就是对整个数据库实例加锁&#xff0c;加锁后整个实例就处于只读状态&#xff0c;后续的DML的写语句&#xff0c;DDL语 句&#xff0c;已经更新操作的事务提交语句都将被阻塞。 其典型的使用场景是做全库的逻辑备份&#xff0c;对所有的表进行锁定&#xff…

go数据格式-JSON、XML、MSGPack

1. JSON json是完全独立于语言的文本格式&#xff0c;是k-v的形式 name:zs应用场景&#xff1a;前后端交互&#xff0c;系统间数据交互 json使用go语言内置的encoding/json 标准库编码json使用json.Marshal()函数可以对一组数据进行JSON格式的编码 func Marshal(v interface{}…

springboot与springcloud之间的版本对应关系

https://start.spring.io/actuator/info 当然&#xff0c;你可以直接在&#xff1a; https://spring.io/projects/spring-cloud 上看文档查询&#xff0c; 不过&#xff0c;最后应该是调到这里的&#xff1a; https://github.com/spring-cloud/spring-cloud-release/wiki/Suppo…

YoloV8改进策略:Block改进|DCNv4最新实践|高效涨点|完整论文翻译

摘要 涨点效果:在我自己的数据集上,mAP50 由0.986涨到了0.991,mAP50-95由0.737涨到0.753,涨点明显! DCNv4是可变形卷积的第四版,速度和v3相比有了大幅度的提升,但是环境搭建有一定的难度,对新手不太友好。如果在使用过程遇到编译的问题,请严格按照我写的环境配置。…

代码随想录算法训练营DAY10 | 栈与队列 (1)

理论基础及Java实现参考文章&#xff1a;栈和队列 一、LeetCode 232 用栈实现队列 题目链接&#xff1a;232.用栈实现队列https://leetcode.cn/problems/implement-queue-using-stacks/ 思路&#xff1a;使用两个栈stack1、stack2实现队列&#xff1b;stack1用来存储入队元素&…

幻兽帕鲁社区服务器搭建架设开服教程(LINUX)

幻兽帕鲁社区服务器搭建架设开服教程&#xff08;LINUX&#xff09; 大家好我是艾西&#xff0c;上一期我给大家分享了windows系统版本的幻兽帕鲁服务器搭建教程。因为幻兽帕鲁这游戏对于服务器的配置有一定的要求很多小伙伴就思考用linux系统搭建的话占用会不会小一点&#x…

从零搭建Vue3 + Typescript + Pinia + Vite + Tailwind CSS + Element Plus开发脚手架

项目代码以上传至码云&#xff0c;项目地址&#xff1a;https://gitee.com/breezefaith/vue-ts-scaffold 文章目录 前言脚手架技术栈简介vue3TypeScriptPiniaTailwind CSSElement Plusvite 详细步骤Node.js安装创建以 typescript 开发的vue3工程集成Pinia安装pinia修改main.ts创…

二叉搜索树,力扣

目录 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 代码补充说明&#xff1a; 代码实现(中序遍历)&#xff1a; 题目地址&#xff1a; 98. 验证二叉搜索树 - 力扣&#xff08;LeetCod…

CSS 星空按钮

<template><button class="btn" type="button"><strong>星空按钮</strong><div id="container-stars"><div id="stars"></div></div><div id="glow"><div class=…

怎么控制Element的数据树形表格展开所有行;递归操作,打造万能数据表格折叠。

HTML <el-button type"success" size"small" click"expandStatusFun"> <span v-show"expandStatusfalse"><i class"el-icon-folder-opened"></i>展开全部</span><span v-show"expan…