Java实现将列表数据导出为PDF文件并添加水印
作者:→_→BéLieve
一、实现效果
二、遇到的问题
1.实现导出PDF主体代码参考:Java纯代码实现导出PDF功能,下图是原作者实现的效果
2.导出报错Font 'STSong-Light' with 'UniGB-UCS2-H' is not recognized.
。参考文末补充内容
网上都是说jar包的版本不对,导致的字体兼容性问题。换了jar包版本发现没效果,后来索性直接把字体下载到本地直接引入。
3.jar包发布到服务器上导出PDF的时候发生报错BOOT-INF/classes!/fonts/SimSun.ttf not exists。
可以看到字体文件在jar目录下面是有的,但是发现classes后面多了个叹号。这是引入外部字体方式不对,后改用问题2参考文章的第三种写法就没问题了。
添加水印参考: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(); }
七、知识补充
itext 生成 PDF使用外部字体
在 Spring Boot 项目中,使用 iTextPDF 库的 BaseFont.createFont() 方法并使用外部字体,您需要将字体文件放置在项目的资源文件夹中,然后使用相对路径来引用它们。
1.绝对路径
BaseFont.createFont("C:\\Windows\\Fonts\\simfang.ttf",BaseFont.IDENTITY_H,BaseFont.EMBEDDED)
2.将字体文件放置在 src/main/resources/fonts 目录下,读取resource目录下的字体
BaseFont baseFont = BaseFont.createFont("classpath:/fonts/simfang.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
注:此方式只有在打成jar时才生效
3.在 Spring Boot 项目中,您可以使用 ResourceLoader 来加载文件,而不必担心文件路径的问题。ResourceLoader 可以在任何环境中工作,无论是在 IDEA 中运行还是在打包的 JAR 文件中运行。
@Service public class MyService { private final ResourceLoader resourceLoader; public MyService(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public void generatePdf() throws IOException, DocumentException { //第一种情况 以 .ttf结尾的字体 Resource resource = resourceLoader.getResource("classpath:/fonts/simfang.ttf"); InputStream inputStream = resource.getInputStream(); // Use the input stream to create the BaseFont object BaseFont baseFont = BaseFont.createFont("simfang.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, inputStream, null); //第二种情况 以 .ttc结尾的字体 Resource resource1 = resourceLoader.getResource("classpath:/fonts/simsun.ttc"); InputStream inputStream = resource1.getInputStream(); // Use the input stream to create the BaseFont object BaseFont baseFont = BaseFont.createFont("simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, inputStream, null); } }
以上就是Java实现将列表数据导出为PDF文件并添加水印的详细内容,更多关于Java列表数据导出为PDF的资料请关注脚本之家其它相关文章!