当前位置: 首页 > news >正文

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

文章目录

  • 前言
  • 一、需求背景
  • 二、方案分析
  • 三、 POI-TL + JFreeChart 实现
    • 3.1 Maven 依赖
    • 3.3 word模板设置
    • 3.2 实现代码
  • 踩坑


前言

在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 Word 报告的功能,并分享一些实际开发中的踩坑经验。
word生成方案:

  1. freemarker+ftl
  2. pot-tl模板替换
  3. poi硬编码

一、需求背景

在之前的文章中,我们已经介绍了如何使用模板替换、复杂表格和图片插入等功能。此次的需求是生成一个包含统计图的 Word 报告,统计图需要根据动态数据生成。面临的主要问题包括:

  1. 选择 Word 生成方案:如何在 Word 中动态插入数据和图表?
  2. 图片插入方案:如何将生成的统计图插入到 Word 中?
  3. 生成统计图表方案:如何根据数据动态生成统计图?

二、方案分析

  1. POI 硬编码
    直接使用 Apache POI 硬编码生成 Word 文档,虽然可行,但代码复杂且难以维护,因此不推荐。

  2. FreeMarker + FTL
    FreeMarker 可以实现文本替换和图片插入,理论上符合需求。但 FTL 模板的维护较为繁琐,尤其是在处理复杂表格和图片时。

  3. POI-TL + JFreeChart
    POI-TL 是一个基于 Apache POI 的模板引擎,支持文本替换、图片插入等功能。结合 JFreeChart 生成统计图,可以很好地满足需求。

三、 POI-TL + JFreeChart 实现

关于JFreeChart请移步使用 JFreeChart 创建动态图表:从入门到实战

3.1 Maven 依赖

首先,需要在项目中引入 POI-TL 和 JFreeChart 的依赖。注意 POI 和 POI-TL 的版本需要对应,否则可能会出现 NoSuchMethod 等错误。

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-excelant</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.jfree</groupId><artifactId>jfreechart</artifactId><version>1.5.3</version></dependency><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.10.0</version></dependency>

3.3 word模板设置

在 Word 模板中,使用占位符标记需要替换的内容。对于图片,需要在占位符前加 @,例如:

  • 替换文本:时间 -> ${date}
  • 插入图片:${@dailyOnlinePic}
  • 条件判断:${?isTrue} 条件内容 ${/isTrue}

3.2 实现代码

以下是核心实现代码:

    private static final String TEMPLATE_PATH = "classpath:template/report.docx";private static final String OUTPUT_DIR = "D:/data/upload/analysis/";private static final String PIC_DIR = OUTPUT_DIR + "pic/";public void getWord(String curDistCode) {try {// 获取模板文件File file = ResourceUtils.getFile(TEMPLATE_PATH);// 构建模板替换的数据Map<String, Object> dataMap = buildTemplateData(curDistCode);// 生成最终文件路径String fileName = UUIDUtil.genUUID32() + ".docx";String filePath = OUTPUT_DIR + fileName;// 使用 POI-TL 渲染模板并保存try (XWPFTemplate template = XWPFTemplate.compile(file, Configure.newBuilder().buildGramer("${", "}").build()).render(dataMap)) {template.writeToFile(filePath);}//上传文件返回附件id} catch (Exception e) {e.printStackTrace();}}private Map<String, Object> buildTemplateData(String curDistCode) throws IOException {Map<String, Object> dataMap = new HashMap<>();// 设置日期和在线总数dataMap.put("date", LocalDate.now());// 查询设备数据dataMap.put("onlineTotal", 100);// 生成每日在线图表DefaultCategoryDataset dailyData = buildDailyDataset(stationByTime);String dailyPicFile = generateChart(dailyData, "监测站总在线数", "小时", "数量", dailyOnlineTxtEnum);dataMap.put("dailyOnlinePic", Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create());return dataMap;}// 构建每日在线图表的数据集private DefaultCategoryDataset buildDailyDataset(List<FireStationByTimeDTO> stationByTime) {DefaultCategoryDataset dataset = new DefaultCategoryDataset();stationByTime.forEach(t -> dataset.addValue(t.getOnlineNum(), "", t.getTime()));return dataset;}// 生成图表并保存为图片private String generateChart(DefaultCategoryDataset dataset, String title, String xAxisLabel, String yAxisLabel, DailyOnlineTxtEnum style) throws IOException {// 设置全局字体(支持中文)StandardChartTheme chartTheme = new StandardChartTheme("CN");chartTheme.setExtraLargeFont(new Font("宋体", Font.PLAIN, 14)); // 标题字体chartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 14));     // 图例字体chartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 12));   // 轴标签字体ChartFactory.setChartTheme(chartTheme);// 创建图表JFreeChart chart = ChartFactory.createLineChart(title, xAxisLabel, yAxisLabel, dataset);setChartStyle(chart, style);// 保存图表为图片String picFile = PIC_DIR + UUIDUtil.genUUID32() + ".png";//int numberOfCategories = dataset.getColumnCount();//int width = Math.max(800, numberOfCategories * 50); // 每个类别宽度为50,最小宽度为800ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400);return picFile;}// 设置图表样式private void setChartStyle(JFreeChart chart) {CategoryPlot plot = chart.getCategoryPlot();chart.setBackgroundPaint(Color.WHITE);plot.setBackgroundPaint(Color.WHITE);plot.setDomainGridlinePaint(Color.LIGHT_GRAY);plot.setRangeGridlinePaint(Color.LIGHT_GRAY);// 设置第一条折线的粗细plot.getRenderer().setSeriesStroke(0, new BasicStroke(5.0f));// 根据样式设置折线颜色plot.getRenderer().setSeriesPaint(0, Color.RED);}

效果
在这里插入图片描述


踩坑

  1. 插入图片如何占位
    与常规文本替换不同,图片插入需要在占位符前加 @,例如 ${@dailyOnlinePic}{{@pic}}
  2. 统计图中文乱码
    JFreeChart 默认不支持中文,需要通过设置全局字体解决:
       // 设置全局字体(支持中文)StandardChartTheme chartTheme = new StandardChartTheme("CN");chartTheme.setExtraLargeFont(new Font("宋体", Font.PLAIN, 14)); // 标题字体chartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 14));     // 图例字体chartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 12));   // 轴标签字体ChartFactory.setChartTheme(chartTheme);

3.统计图横坐标…
如果生成的图片宽度不够,横坐标可能会显示不全。可以通过增加图片宽度解决,插入时等比例缩放:

ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400);
dataMap.put("dailyOnlinePic", 
Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create());

或动态计算需要生成的图片宽度:

int numberOfCategories = dataset.getColumnCount();
int width = Math.max(800, numberOfCategories * 50); // 每个类别宽度为50,最小宽度为800
  1. 模板条件判断
    POI-TL支持复杂的条件判断,例如结合逻辑运算符(&&、||)和比较运算符(==、!=、>、< 等)。
    例如:
{{?isStudent}}{{?age > 18}}您是一名成年学生。{{/age > 18}}
{{/isStudent}}

此次实验boolean好用,字符串==匹配失败。


在这里插入图片描述

相关文章:

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

文章目录 前言一、需求背景二、方案分析三、 POI-TL JFreeChart 实现3.1 Maven 依赖3.3 word模板设置3.2 实现代码 踩坑 前言 在开发过程中&#xff0c;我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart&#xff0c;实现动态生成 W…...

xxl-job的分片广播

目录 xxl-job的分片广播 场景引入 xxl-job简介 xxl-job的部署安装 代码编写 1.导入依赖 2.yml文件编写 3.编写xxl-job执行器配置类&#xff0c;维护一个xxl-job执行器的bean 4.编写第一个任务&#xff0c;任务名字叫firstJob 5.进入服务端&#xff0c;增加执行器和任务…...

MobaXterm破解会话上限限制

1. 下载安装包MobaXterm-Keygen 下载路径&#xff1a; https://gitcode.com/gh_mirrors/mob/MobaXterm-Keygen 2. 搭建python3环境 window下python3环境搭建可参考网站&#xff1a; https://blog.csdn.net/enteracity/article/details/135479689 3. 生成文件Custom.mxtpro…...

vscode设置保存时自动缩进和格式化

参考博客 如何在 VSCode 中自动缩进你的代码 | Linux 中国 省流 使用 Ctrl Shift P 来打开命令模式&#xff0c;搜索 Open User Settings 并按下回车你需要搜索 Auto Indent&#xff0c;并在 “编辑器&#xff1a;自动缩进(Editor: Auto Indent)” 中选择 “全部(Full)”P…...

一键查看电脑各硬件详细信息 轻松查看电脑硬件参数

今天为大家推荐两款非常实用的电脑硬件查看软件&#xff0c;它们能够一键快速查看电脑的各种配置信息&#xff0c;使用起来非常方便。 一键查看电脑各硬件详细信息 这款软件是绿色版的&#xff0c;无需安装&#xff0c;打开即可使用&#xff0c;文件大小仅为900多KB&#xff0…...

【C++11】lambda和包装器

1.新的类功能 1.1默认的移动构造和移动赋值 原来C类中&#xff0c;有6个默认成员函数&#xff1a;构造函数/析构函数/拷⻉构造函数/拷⻉赋值重载/取地址重 载/const 取地址重载&#xff0c;最后重要的是前4个&#xff0c;后两个⽤处不⼤&#xff0c;默认成员函数就是我们不写…...

react redux用法学习

参考资料&#xff1a; https://www.bilibili.com/video/BV1ZB4y1Z7o8 https://cn.redux.js.org/tutorials/essentials/part-5-async-logic AI工具&#xff1a;deepseek&#xff0c;通义灵码 安装相关依赖&#xff1a; 使用redux的中间件&#xff1a; npm i react-reduxreact-…...

前端HTML标签 meta中常见的一些属性

meta中常见的一些属性 <meta/> 标签的属性 <meta/> 是什么&#xff1f; <meta/> 标签主要用于表示和当前文档相关的 元数据 信息。 而 元数据&#xff08;metadata&#xff09;&#xff0c;简单的来说就是描述数据的数据。例如&#xff0c;一个 HTML 文件是一…...

127,【3】 buuctf [NPUCTF2020]ReadlezPHP

进入靶场 吓我一跳 查看源码 点击 审计 <?php// 定义一个名为 HelloPhp 的类&#xff0c;该类可能用于执行与日期格式化相关的操作 class HelloPhp {// 定义一个公共属性 $a&#xff0c;用于存储日期格式化的模板public $a;// 定义一个公共属性 $b&#xff0c;用于存储…...

继承(python)

一、基础知识 &#xff08;一&#xff09;定义&#xff1a;子类能继承父类所有的公有属性和公有方法&#xff08;先使用子类的方法、属性&#xff09; &#xff08;二&#xff09;格式&#xff1a; class 子类名&#xff08;父类名&#xff09;&#xff1a; #父类 class Ph…...

驱动开发系列36 - Linux Graphics 2D 绘制流程

一: 概述 在Linux中,2D绘制流程是操作系统、图形库、显示协议、驱动程序等多个组件协调工作的结果。整体流程如下步骤所示: 1. 客户端请求:客户端程序(如GTK、Qt应用程序)通过X11协议与Xorg-Server通信(或通过Wayland协议与Wayland合成器通信)、请求绘制2D图形,比如绘制…...

STL函数算法笔记

STL函数算法笔记 今天我们来学习的是STL库中的一些函数。首先,STL这个东西大家一定非常熟悉,里面很多的数据结构都帮了大家不少忙,那么今天我们就来说几个重要的数据结构。 向量 向量,也就是数据结构vector,你也可以称之为动态数组,本质跟数组差不多,只不过有一些好处…...

【Vue】在Vue3中使用Echarts的示例 两种方法

文章目录 方法一template渲染部分js部分方法一实现效果 方法二template部分js or ts部分方法二实现效果 贴个地址~ Apache ECharts官网地址 Apache ECharts示例地址 官网有的时候示例显示不出来&#xff0c;属于正常现象&#xff0c;多进几次就行 开始使用前&#xff0c;记得先…...

小红书自动化:如何利用Make批量生成爆款笔记

小红书自动化&#xff1a;如何利用Make制作个人自媒体中心&#xff0c;批量生成爆款笔记 引言 在如今信息爆炸的时代&#xff0c;如何高效地获取和分享优质内容&#xff0c;成为了每位自媒体工作者必须面对的挑战。你是否想过&#xff0c;如果能够将这项繁复的工作实现自动化…...

学习率调整策略 | PyTorch 深度学习实战

前一篇文章&#xff0c;深度学习里面的而优化函数 Adam&#xff0c;SGD&#xff0c;动量法&#xff0c;AdaGrad 等 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 本篇文章内容来自于 强化学习必修课&#xff1a;引…...

DeepSeekMoE 论文解读:混合专家架构的效能革新者

论文链接&#xff1a;DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models 目录 一、引言二、背景知识&#xff08;一&#xff09;MoE架构概述&#xff08;二&#xff09;现有MoE架构的问题 三、DeepSeekMoE架构详解&#xff08;一&a…...

以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案:

以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案&#xff1a; 一、系统概述 本系统采用双巨控GRM241Q模块构建4G无线物联网络&#xff0c;实现山上液位数据实时传输至山下水泵站&#xff0c;通过预设逻辑自动控制水泵启停&#xff0c;同时支持APP远程监控及人工…...

【JVM详解五】JVM性能调优

示例&#xff1a; 配置JVM参数运行 #前台运行 java -XX:MetaspaceSize-128m -XX:MaxMetaspaceSize-128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio8 - XX:UseConcMarkSweepGC -jar /jar包路径 #后台运行 nohup java -XX:MetaspaceSize-128m -XX:MaxMetaspaceS…...

2.10日学习总结

题目一&#xff1a; AC代码 #include <stdio.h>#define N 1000000typedef long long l;int main() {int n, m;l s 0;l a[N 1], b[N 1];int i 1, j 1;scanf("%d %d", &n, &m);for (int k 1; k < n; k) {scanf("%lld", &a[k]);…...

疯狂前端面试题(四)

一、Ajax、JSONP、JSON、Fetch 和 Axios 技术详解 1. Ajax&#xff08;异步 JavaScript 和 XML&#xff09; 什么是 Ajax&#xff1f; Ajax 是一种用于在不刷新页面的情况下与服务器进行数据交互的技术。它通过 XMLHttpRequest 对象实现。 优点 - 支持同步和异步请求。 - 能…...

解决k8s集群中containerd运行时拉取HTTP私有Harbor镜像的配置难题

1. 为什么需要配置HTTP私有Harbor镜像拉取 最近在帮客户部署Kubernetes集群时&#xff0c;遇到了一个典型问题&#xff1a;使用containerd作为容器运行时&#xff0c;无法从内网HTTP协议的Harbor私有仓库拉取镜像。这个问题其实很常见&#xff0c;特别是很多企业内网环境中&…...

HackRF玩家必备:PortaPack H2固件刷写与Mayhem固件配置全攻略

HackRF玩家进阶指南&#xff1a;PortaPack H2固件刷写与Mayhem实战配置 无线电爱好者们对HackRF的探索从未停止&#xff0c;而PortaPack H2扩展板的出现让这款开源SDR设备真正实现了"口袋实验室"的愿景。不同于市面上简单的使用说明&#xff0c;本文将带你深入理解Po…...

OpenClaw定时任务详解:GLM-4.7-Flash每日自动生成工作报告

OpenClaw定时任务详解&#xff1a;GLM-4.7-Flash每日自动生成工作报告 1. 为什么需要自动化日报系统 上周三晚上11点&#xff0c;我盯着空白的周报文档发呆——明明这周完成了3个需求迭代和2次跨部门协作&#xff0c;却怎么都想不起具体细节。翻遍Git记录、邮件和会议纪要才勉…...

74HC595驱动8位数码管实战:从查找表到动态扫描的完整流程

74HC595驱动8位数码管实战&#xff1a;从查找表到动态扫描的完整流程 在嵌入式系统开发中&#xff0c;数码管显示是最基础也最考验硬件理解能力的环节之一。记得我第一次尝试用74HC595驱动数码管时&#xff0c;被那个"看似简单却暗藏玄机"的动态扫描原理折磨了整整三…...

5分钟解决PDF转Markdown痛点:Marker工具全场景应用指南

5分钟解决PDF转Markdown痛点&#xff1a;Marker工具全场景应用指南 【免费下载链接】marker 一个高效、准确的工具&#xff0c;能够将 PDF 和图像快速转换为 Markdown、JSON 和 HTML 格式&#xff0c;支持多语言和复杂布局处理&#xff0c;可选集成 LLM 提升精度&#xff0c;适…...

破局与重构:基于“智慧大脑”的企业全面数据化经营深度解构(PPT)

“在数字时代&#xff0c;企业最大的风险不是数据的匮乏&#xff0c;而是决策依然依赖经验直觉而非数据驱动。” —— 这份《数字化建设企业经营解决方案》文档&#xff0c;不仅是一份技术蓝图&#xff0c;更是对传统企业经营管理模式的一次彻底颠覆。它描绘了一个从“人治”迈…...

OpenUSD终极渲染器切换指南:Storm vs Prman性能深度对比

OpenUSD终极渲染器切换指南&#xff1a;Storm vs Prman性能深度对比 【免费下载链接】OpenUSD Universal Scene Description 项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD OpenUSD&#xff08;Universal Scene Description&#xff09;作为强大的3D场景描…...

工业Python网关配置不是写代码,是做工程!揭秘ISO/IEC 62443合规配置清单(仅限首批200家制造企业内部流出)

第一章&#xff1a;工业Python网关配置不是写代码&#xff0c;是做工程&#xff01;在工业现场&#xff0c;Python网关绝非“跑个脚本就能连PLC”的玩具级工具——它是一套融合协议适配、资源约束、故障自愈与长期稳定运行的系统工程。配置的本质&#xff0c;是定义设备生命周期…...

Bing Wallpaper自动化部署:GitHub Actions与持续集成

Bing Wallpaper自动化部署&#xff1a;GitHub Actions与持续集成 【免费下载链接】bing-wallpaper 项目地址: https://gitcode.com/gh_mirrors/bi/bing-wallpaper Bing Wallpaper项目是一个专注于收集和展示Bing每日壁纸的开源项目&#xff0c;通过自动化部署可以确保壁…...

4大维度解锁TrafficMonitor插件扩展能力:定制化系统监控全攻略

4大维度解锁TrafficMonitor插件扩展能力&#xff1a;定制化系统监控全攻略 【免费下载链接】TrafficMonitorPlugins 用于TrafficMonitor的插件 项目地址: https://gitcode.com/gh_mirrors/tr/TrafficMonitorPlugins 价值定位&#xff1a;为什么需要TrafficMonitor插件系…...