springboot使用itextpdf+jfreechart制作PDF文档

news/2024/7/7 3:34:43/文章来源:https://www.cnblogs.com/pjh-blogs/p/18278329

1. springboot引入的依赖组件

项目中需要引入itextpdf和jfreechart两个组件,版本根据项目所需进行引入,maven组件版本查询可根据如下地址进行查询:maven组件查询

    <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency><dependency><groupId>org.jfree</groupId><artifactId>jfreechart</artifactId><version>1.5.4</version></dependency>

2. PDF中的图表制作

项目中根据接口进行导出PDF,示例如下:

点击查看代码
    @GetMapping("/export/pdf")public void exportPDFReport(HttpServletResponse response)throws Exception {Document document = new Document(PageSize.A4, 36, 36, 36, 36);try {response.setContentType("application/pdf");response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("测试报告", "UTF-8") + ".pdf");OutputStream os = new BufferedOutputStream(response.getOutputStream());PdfWriter writer = PdfWriter.getInstance(document, os);// pdf报告标题addTitlePage(document);// 饼图addPieChart(document, writer);// 条形图addBarChart(document, writer);// 折线图addLineChart(document, writer);// 数据表格addDataTable(document,"任务标题","张三");document.open();document.close();os.close();} catch (Exception e) {document.close();throw new RuntimeException("导出报告异常,错误信息:" + e.getMessage());}finally {document.close();}}
- 报告标题制作,示例如下:
private static void addTitlePage(Document document) throws DocumentException, IOException {// 创建一个BaseFont对象,指定字体路径和编码以支持中文BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);// 使用上面的字体创建一个Font对象Font chineseFont = new Font(bfChinese, 18, Font.BOLD, BaseColor.BLACK);// 使用支持中文的字体创建段落Paragraph title = new Paragraph("测试计划", chineseFont);title.setAlignment(Element.ALIGN_CENTER);Chapter chapter = new Chapter(title, 1);chapter.setNumberDepth(0);document.add(chapter);
}

注意:如果需要上下层级保持一定的间隔,需要加入如下代码,主要作用是为上下内容保持一定的间隙,示例如下:

document.add(new Paragraph(" ", new Font(Font.FontFamily.HELVETICA, 12))); // 增加间距
  • 报告插入饼图图表,示例如下:
    private static void addPieChart(Document document, PdfWriter writer) throws DocumentException {DefaultPieDataset dataset = new DefaultPieDataset();dataset.setValue("Category 1", 30);dataset.setValue("Category 2", 20);dataset.setValue("Category 3", 50);JFreeChart pieChart = ChartFactory.createPieChart("Sample Pie Chart",dataset,true,true,false);Image chartImage = getImageFromChart(pieChart, writer);placeImageInDocument(document, chartImage);}
  • 报告插入条形图,示例如下:
    private static void addBarChart(Document document, PdfWriter writer) throws DocumentException {DefaultCategoryDataset dataset = new DefaultCategoryDataset();dataset.addValue(400, "Series 1", "Category 1");dataset.addValue(150, "Series 1", "Category 2");dataset.addValue(120, "Series 1", "Category 3");JFreeChart barChart = ChartFactory.createBarChart("Sample Bar Chart","Category","Value",dataset,PlotOrientation.VERTICAL,true,true,false);CategoryPlot plot = barChart.getCategoryPlot();BarRenderer renderer = (BarRenderer) plot.getRenderer();renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());renderer.setDefaultItemLabelsVisible(true); // 数值标签可见// 动态设置y轴范围// 计算最大值double maxValue = 0;for (int i = 0; i < dataset.getRowCount(); i++) {for (int j = 0; j < dataset.getColumnCount(); j++) {Number value = dataset.getValue(i, j);if (value != null && value.doubleValue() > maxValue) {maxValue = value.doubleValue();}}}// 动态设置y轴范围NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();rangeAxis.setAutoRangeIncludesZero(false); // 不包括0rangeAxis.setAutoRange(true); // 自适应范围rangeAxis.setUpperBound(maxValue * 1.1); // 设置上限为最大值的1.1倍或其他适当值Image chartImage = getImageFromChart(barChart, writer);placeImageInDocument(document, chartImage);}
  • 报告插入折线图,示例如下:
    public void addLineChart(Document document, PdfWriter writer) throws DocumentException {DefaultCategoryDataset dataset = new DefaultCategoryDataset();dataset.addValue(400, "Series 1", "Category 1");dataset.addValue(150, "Series 1", "Category 2");dataset.addValue(120, "Series 1", "Category 3");JFreeChart lineChart = ChartFactory.createLineChart("","","",dataset,PlotOrientation.VERTICAL,true,true,false);CategoryPlot plot = lineChart.getCategoryPlot();plot.setOutlineVisible(false); // 关闭绘图区域的边框线// 启用Y轴网格线的显示plot.setRangeGridlinesVisible(true);// 设置Y轴网格线颜色和样式plot.setRangeGridlinePaint(Color.GRAY); // 设置网格线颜色为灰色// 创建虚线效果float[] dashPattern = {5f, 5f}; // 定义虚线的模式BasicStroke dashed = new BasicStroke(0.1f, // 线宽BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,10.0f, // 斜接长度dashPattern, // 虚线模式0.0f); // 虚线相位// 应用虚线设置到Y轴网格线plot.setRangeGridlineStroke(dashed);// 如果需要,也可以启用并设置X轴网格线的颜色和样式plot.setDomainGridlinesVisible(false);// 自定义X轴标签,只显示原始name,去除索引部分CategoryAxis domainAxis = new CategoryAxis() {@Overrideprotected TextBlock createLabel(Comparable category, float width, RectangleEdge edge, Graphics2D g2) {String label = category.toString();// 假设唯一标识符是原始name后跟一个空格和序号,这里我们只提取原始nameString originalName = label.substring(0, label.lastIndexOf('_')); // 假设'_'之前的是原始namereturn super.createLabel(originalName, width, edge, g2);}};// 创建一个新的字体,并设置字体大小为12java.awt.Font labelFont = new java.awt.Font("宋体", java.awt.Font.PLAIN, 8);// 将新的字体应用到X轴标签上domainAxis.setTickLabelFont(labelFont);domainAxis.setLabelFont(labelFont);// 设置自定义的标签生成器到图表的X轴plot.setDomainAxis(domainAxis);// 动态设置y轴范围// 计算最大值double maxValue = 0;for (int i = 0; i < dataset.getRowCount(); i++) {for (int j = 0; j < dataset.getColumnCount(); j++) {Number value = dataset.getValue(i, j);if (value != null && value.doubleValue() > maxValue) {maxValue = value.doubleValue();}}}// 动态设置y轴范围NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();rangeAxis.setAutoRangeIncludesZero(false); // 不包括0rangeAxis.setAutoRange(true); // 自适应范围// 设置Y轴的刻度单位// 检查是否所有数据都是0if (maxValue == 0.0) {rangeAxis.setUpperBound(1); // 如果是,则手动设置一个上限,例如1} else {rangeAxis.setUpperBound(maxValue * 1.5); // 否则,设置上限为最大值的1.5倍}Image chartImage = getImageFromChart(lineChart, writer);placeImageInDocument(document, chartImage);}
  • 报告插入数据表格,示例如下:
   private static void addDataTable(Document document, String taskTitle,String nickName) throws Exception {Font font = new Font(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED));Font fontHeader = new Font(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), 12, Font.BOLD);PdfPTable table = new PdfPTable(6);// 设置每列的宽度float[] columnWidths = {2, 8, 3, 2, 4, 2}; // 定义每列的相对宽度比例table.setWidths(columnWidths);// 设置表格行高
//        table.getDefaultCell().setFixedHeight(80);table.setTotalWidth(600);PdfPCell cell1 = new PdfPCell(new Phrase("ID",fontHeader));cell1.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell1);PdfPCell cell2 = new PdfPCell(new Phrase("任务预览",fontHeader));cell2.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell2);PdfPCell cell3 = new PdfPCell(new Phrase("任务状态",fontHeader));cell3.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell3);PdfPCell cell4 = new PdfPCell(new Phrase("缺陷数",fontHeader));cell4.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell4);PdfPCell cell5 = new PdfPCell(new Phrase("关联产品与版本",fontHeader));cell5.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell5);PdfPCell cell6 = new PdfPCell(new Phrase("需求数",fontHeader));cell6.setHorizontalAlignment(Element.ALIGN_CENTER);table.addCell(cell6);// 添加其它字段的值// IDPdfPCell cell1Value = new PdfPCell(new Phrase("1", font));cell1Value.setHorizontalAlignment(Element.ALIGN_CENTER);// 垂直居中cell1Value.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell1Value);// 任务预览PdfPCell progressBarCell = createProgressBarCellWithDetails(10,2, 2, 1,1,3, taskTitle,nickName); // 假设Pass:2个, Fail:1个// 垂直居中progressBarCell.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(progressBarCell);// 任务状态PdfPCell cell3Value = new PdfPCell(new Phrase("进行中", font));cell3Value.setHorizontalAlignment(Element.ALIGN_CENTER);// 垂直居中cell3Value.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell3Value);// 缺陷数// 任务状态PdfPCell cell4Value = new PdfPCell(new Phrase("10", font));cell4Value.setHorizontalAlignment(Element.ALIGN_CENTER);// 垂直居中cell4Value.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell4Value);// 关联产品与版本PdfPCell cel54Value = new PdfPCell(new Phrase("产品A(v1.0.0)", font));cel54Value.setHorizontalAlignment(Element.ALIGN_CENTER);// 垂直居中cel54Value.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cel54Value);// 需求数PdfPCell cell6Value = new PdfPCell(new Phrase("10", font));cell6Value.setHorizontalAlignment(Element.ALIGN_CENTER);// 垂直居中cell6Value.setVerticalAlignment(Element.ALIGN_MIDDLE);table.addCell(cell6Value);document.add(table);}

3. 导出PDF制作完成后结果

  • 结果呈现示例图片如下:
    image

4.总结分析

本次分享主要作为一个经验分享,该内容完全由本人真实项目中所使用及遇到的问题所描述,如有不了解或者想要深入研究分析的可以评论及联系,下面我结合本次实现该功能所遇到的问题总结如下几点进行避免踩坑:

  • 图表中的中文显示问题
    如上诉的图表中涉及到的中文,有可能在开发中无法显示,这里面主要是因为使用到的Font类需要自定义中文显示类型,如:
// 创建一个BaseFont对象,指定字体路径和编码以支持中文BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

如果你遇到了Series或者toolti中文显示不出来,则需要使用JFreeChart进行一个自定义的字体显示,如下:

默认的是Graphics2D graphics2d = new PdfGraphics2D(template, width, height, new DefaultFontMapper());改写如下:
需要自定义FontMapper
// 创建一个自定义的字体映射DefaultFontMapper mapper = new DefaultFontMapper() {@Overridepublic BaseFont awtToPdf(java.awt.Font font) {try {// 指定一个支持中文的PDF字体,例如使用iText内置的STSong-Light字体// 并设置编码以支持中文return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);} catch (Exception e) {e.printStackTrace();return super.awtToPdf(font);}}};Graphics2D graphics2d = new PdfGraphics2D(template, width, height, mapper);
  • PDF本身受到宽距和高距限制,导致字符长无法显示完整问题
    问题:
    在比较拥挤的页面时,发现图标的X轴标签显示不完整,或者呈现...字样
    解决方式:
CategoryPlot plot = barChart.getCategoryPlot();plot.setOutlineVisible(false); // 关闭绘图区域的边框线CategoryAxis domainAxis = plot.getDomainAxis();// 创建一个新的字体,并设置字体大小为12java.awt.Font labelFont = new java.awt.Font("宋体", java.awt.Font.PLAIN, 8);// 将新的字体应用到X轴标签上domainAxis.setTickLabelFont(labelFont);domainAxis.setLabelFont(labelFont);
//        domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);  // 设置标签角度为45度,以便显示较长的标签BarRenderer renderer = (BarRenderer) plot.getRenderer();renderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());renderer.setDefaultItemLabelsVisible(true); // 数值标签可见
  • 内容宽距和高距设置不合理导致无法显示问题
    问题:我们在制作各种数据内容的时候,例如想在一行既显示图表,有显示表格,这样的话就会涉及到两个表所需要的实际宽距,如果不合理就会导致超出后无法显示内容
    解决方式:
根据不同表所需要的宽距和高距设置进行合理调整,并留出一定的空间public Image getImageFromChart(JFreeChart chart, PdfWriter writer,int cellWidth, int cellHeight) throws DocumentException {int width = cellWidth - 20; // 减去一些边距int height = cellHeight - 20; // 减去一些边距chart.setBackgroundPaint(Color.white); // 设置绘图区域的背景色为白色chart.getPlot().setBackgroundPaint(Color.white); // 设置图表区域的背景色为白色
//        if (chart.getLegend() != null) {
//            chart.getLegend().setItemFont(new java.awt.Font("宋体", java.awt.Font.PLAIN, 10)); // 设置图例的字体
//        }PdfContentByte contentByte = writer.getDirectContent();PdfTemplate template = contentByte.createTemplate(width, height);// 创建一个自定义的字体映射DefaultFontMapper mapper = new DefaultFontMapper() {@Overridepublic BaseFont awtToPdf(java.awt.Font font) {try {// 指定一个支持中文的PDF字体,例如使用iText内置的STSong-Light字体// 并设置编码以支持中文return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);} catch (Exception e) {e.printStackTrace();return super.awtToPdf(font);}}};Graphics2D graphics2d = new PdfGraphics2D(template, width, height, mapper);Rectangle2D rectangle2d = new Rectangle2D.Double(0, 0, width, height);chart.draw(graphics2d, rectangle2d);graphics2d.dispose();return Image.getInstance(template);}

结论:希望本博客能够为你开发此功能提供有需要的帮助,如果遇到问题,也可联系,看到的话会提供相应的帮助,长路漫漫,程序不断,携手共进,创造辉煌,😄

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring趣玩

自定义banner 先上图片,替换spring启动标志;或者也可以在启动类代码设置Banner也可以通过设置springApplication.setBannerMode(Banner.Mode.OFF);关闭打印横幅ApplicationRunner和@PostConstruct ApplicationRunner 是一种灵活的机制,可以用来执行数据库迁移、预加载数据、…

图论初步与可视化

本讲将简要介绍图论中的基本概念,并主要讲解图论中的最短路径问题。以及如何将图论可视化 目录一、图论的概念二、在线作图网站1.index介绍2.Node Count介绍3.Graph data三、Matlab作无向图1.无权图(每条边的权重默认为1)2.利用字符串做无权图3.有权图四、Matlab作有向图 一…

使用不同函数打印torch.nn模型——print(model),named_children(),named_modules():

创建一个具有三级嵌套的模型,结构如图:import torch import torch.nn as nn# 定义子子模块 class SubSubModule(nn.Module):def __init__(self):super(SubSubModule, self).__init__()self.conv = nn.Conv2d(3, 3, kernel_size=3, padding=1)def forward(self, x):return sel…

[LeetCode] 189. Rotate Array

无敌的切片他又来了。 class Solution:def rotate(self, nums: List[int], k: int) -> None:"""Do not return anything, modify nums in-place instead."""#0if k ==0:pass#elselenn = len(nums)k = k % lennnums[:] =nums[lenn -k:] + nums…

Centos双网卡冗余绑定

1. 前言 关于双网卡绑定,前前后后踩过不少的坑,虽然这是 RHCE 中的一道题,但是在实践中碰到问题也够喝一壶的。 在实践中,虚拟机、物理机都做过,但是不尽相同,大部分的坑也集中在这里,本文长期更新关于网卡绑定中遇到的问题及处理方法。 现在的服务器默认都配备 4 张 千…

Java 将Markdown文件转换为Word和PDF文档

Markdown 凭借其简洁易用的特性,成为创建和编辑纯文本文档的常用选择。但某些时候我们需要更加精致的展示效果,例如在专业分享文档或打印成离线使用的纸质版时,就需要将Markdown文件以其他固定的文档格式呈现。通过将 Markdown 转换为 Word 和 PDF 格式,可以得到更多的格式…

k8s-09-Deployment

Deployment微服务化:将一个大规模系统拆分成各个独立运行的组件更新 pod:直接删除所有现有的 pod, 然后创建新的 pod。 先创建新的 pod, 并等待它们成功运行之后, 再删除旧的 pod。 按顺序创建新的 pod, 然后逐渐删除旧的 pod。第 1 种会导致应用程序在一定的时间内不可用。…

IOT2050基本使用和指令集

安装系统镜像 -- debianIOT镜像地址下载及镜像版本说明Win32 disk Imager下载,用于SD卡安装镜像文件使用win32磁盘映像工具镜像操作如下:如果需要把SD卡中的镜像写入到eMMC卡:将烧录好的SD卡插入设备,修改SD卡启动顺序为默认启动设备。 如果已经存在emmc系统,则先格式化em…

K8S学习教程(一):使用PetaExpress云服务器安装Minikube 集群

什么是MinikubeMinikube是一款工具,主要用于在本地运行 Kubernetes 集群。Kubernetes 开源的平台,用于自动化容器化应用的部署、扩展和管理,而Minikube 使得开发人员能够在本地机器上轻松创建一个单节点的 Kubernetes 集群,从而方便开发、测试和学习 Kubernetes。我们看下如…

蓝牙芯片认证

前言: BLE产品工作在2.4G频段。产品具有蓝牙功能并且在产品外观上标明蓝牙标志,则必须通过认证。 各个国家的认证标准不同,但是大同小异。例如FCC/MIC/CE/BQB等。具体需要过某个认证可以查看出口国家。 认证可以分为传导和空气耦合。传导是测试芯片发射信号的波形;耦合是测…

Linux下二进制可执行文件分析 (nm,readelf,objdump 命令使用)

最近在调试一些问题,发现几个命令很实用,记录一下。 一 背景也许大家都遇到过这种场景,就是有二进制代码,比如深度分析下此文件到底是什么格式的图片等,这篇文章就记录我分析下二进制可执行文件的过程,已经自己读写二进制文件的一些坑。分析的二进制执行文件为linux下的可…

hypernetwork在SD中是怎么工作的

大家在stable diffusion webUI中可能看到过hypernetwork这个词,那么hypernetwork到底是做什么用的呢?大家在stable diffusion webUI中可能看到过hypernetwork这个词,那么hypernetwork到底是做什么用的呢? 简单点说,hypernetwork模型是用于修改样式的小型神经网络。 什么是…

使用 ROS2的多机器人探索

原文链接:https://www.youtube.com/watch?v=J0RZP_xJ3XA This video shows a demonstration of the SOS project, dedicated to forest fire detection using a fleet of robots. Several important issues are addressed. 这段视频展示了SOS项目的演示,该项目致力于使用机…

企业生产环境Nacos集群部署示例

Nacos运行环境需要jdk环境,集群各节点服务器需安装jdk1.8: jdk-8u341-linux-x64.tar 第一步:上次安装包 第二步:解压 sudo tar -zxvf jdk-8u341-linux-x64.tar.gz 第三步: 配置环境变量sudo vim /etc/profile 第四步:添加以下内容 export JAVA_HOME=/usr/local/jdk1.8.0…

Identity-aware Graph Neural Networks

目录概ID-GNNYou J., Gomoes-Selman J., Ying R. and Leskovec J. Identity-aware graph neural networks. AAAI, 2021.概 提出了一种能够超越 1-WL-Test 的 GNN. ID-GNNID-GNN 的 motivation 主要如下:主要到, 传统的 MPNN, 即第 \(k\) 层: \[\mathbf{m}_u^{(k)} = \text{MSG}…

AMM论文阅读笔记

AMM: Attentive Multi-field Matching for News Recommendation论文阅读笔记 Abstract 现存的问题: ​ 个性化新闻推荐是帮助用户找到感兴趣新闻的关键技术,而如何精确匹配用户兴趣和候选新闻是新闻推荐的核心。现有研究一般通过聚合用户浏览过的新闻来学习用户的兴趣向量,…

没有MAC电脑,如何申请苹果开发证书、上架APP Store?

【引言】 使用uni-app进行跨平台APP开发时,苹果ios平台最终还是要通过APP Store渠道发布,调试时uni-app基座也必须使用开发者证书签名后才能安装。对于使用MAC电脑的开发者,倒也不存在什么大障碍,照着文档操作就行,但是对于不使用MAC电脑,身边也没有MAC电脑,采购预算又紧…

知网文献下载助手 ——油猴脚本推荐

知网文献下载收费太贵了,只能逼我去找脚本来下载了。 在Greasyfork尝试了很多个脚本,坑爹的比较多,对比下来,我觉得这个“知网下载助手”比较容易用, 当然是纯免费的,需要的可以试试:知网下载助手https://greasyfork.org/zh-CN/scripts/492511

类人型自主机器人会打太极拳,但功夫很弱-斯坦福HumanPlus机器人

原文链接:https://newatlas.com/robotics/autonomous-humanoid-robot-shadows-humans/ The HumanPlus, with a 0-0 record, lands a 1, 2 left, right punch Stanford Humanplus HumanPlus以0比0的战绩,命中了1次左、右两拳。 斯坦福…