文章目录
- [1 思路](#1 思路)
-
- [1.1 概述](#1.1 概述)
- [1.2 支持的图表类型](#1.2 支持的图表类型)
- [1.3 特性](#1.3 特性)
- [2 准备模板](#2 准备模板)
- [3 导入依赖](#3 导入依赖)
- [4 图表生成工具类 ChartWithChineseExample](#4 图表生成工具类 ChartWithChineseExample)
- [5 业务层 OfficeServicel](#5 业务层 OfficeServicel)
- [6 通用工具类 OfficeUtils](#6 通用工具类 OfficeUtils)
- [7 控制层 OfficeController](#7 控制层 OfficeController)
- [8 导出效果](#8 导出效果)
1 思路
JFreeChart :
JFreeChart是一个开源的Java图表库,专为JAVA平台设计,用于生成高质量的2D图表。
1.1 概述
- JFreeChart是一个完全使用JAVA语言编写的图表绘制类库。
- 它最初由David Gilbert创建,自2001年以来一直在持续开发和更新,目前已成为Java社区中广泛使用的图表库之一。
- JFreeChart是一个开源项目,遵循GNU通用公共许可证(LGPL),允许在专有应用程序中使用。
1.2 支持的图表类型
- JFreeChart支持多种图表类型,包括但不限于:
- 饼图(Pie charts)
- 柱状图(Bar charts)
- 散点图(Scatter plots)
- 时序图(Time series)
- 甘特图(Gantt charts)
- 线形图(Line charts)
- 气泡图(Bubble charts)
- 热力图(Heatmaps)
1.3 特性
- 定制能力:提供大量的定制选项,包括颜色、字体、标签、图例、网格线、数据点等,以满足各种设计需求。
- 数据源:接受各种数据结构作为输入,如数组、列表或CategoryDataset和TimeSeriesDataset对象。
- 输出类型:支持多种输出类型,包括Swing组件、图像文件(PNG、JPEG)、矢量图形文件格式(PDF、EPS、SVG)等。
- 交互性:具有一定的交互功能,如缩放、平移等。
通过 JFreeChart 创建图表,将图表转换为图像格式(如PNG或JPEG),然后将图像解析成InputStream 写入到Word文档的相应位置中。
2 准备模板
3 导入依赖
xml
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.3</version>
</dependency>
4 图表生成工具类 ChartWithChineseExample
在使用org.jfree.chart
库生成图表时,如果遇到中文无法正常显示的问题,通常是字体设置的问题。JFreeChart默认使用的字体可能不支持中文字符。要解决这个问题,你需要指定一个支持中文的字体。以下是解决此问题的一般步骤:
步骤 1: 准备字体文件
首先,你需要一个支持中文的TrueType字体文件(.ttf
),如宋体(SimSun.ttf
)、微软雅黑(msyh.ttf
)等。这些字体文件通常可以在Windows系统的C:\Windows\Fonts
目录下找到,或者你可以从互联网上下载。
字体文件包可以从这里下载:office字体文件包
步骤 2: 注册字体到FontFactory
在你的Java程序中,使用FontFactory.register()
方法注册你的中文字体文件。例如,如果你有SimSun.ttf
这个字体文件,可以这样做:
java
/**
* 注册中文字体
*/
public static void registerChineseFont() {
// 注册中文字体(这里假设已经将字体文件放置在项目的resources目录下)
InputStream fontStream = ChartWithChineseExample.class.getResourceAsStream("/font/SIMSUN.TTC"); // 路径根据实际情况调整
try {
Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream).deriveFont(Font.PLAIN, 12);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(customFont);
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
}
步骤 3: 设置图表具体位置的字体
柱状图:
java
// 示例字体为宋体,常规,14号
Font axisLabelFont = new Font("SimSun", Font.PLAIN, 14);
// X轴
chart.getCategoryPlot().getDomainAxis().setLabelFont(axisLabelFont);
chart.getCategoryPlot().getDomainAxis().setTickLabelFont(axisLabelFont);
// Y轴
chart.getCategoryPlot().getRangeAxis().setLabelFont(axisLabelFont);
chart.getCategoryPlot().getRangeAxis().setTickLabelFont(axisLabelFont);
饼图:
java
chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));
chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));
// 获取饼图的plot对象,以便进行进一步定制
PiePlot3D plot = (PiePlot3D) chart.getPlot();
// 设置标签字体
plot.setLabelFont(new Font("SimSun", Font.PLAIN, 14));
// 设置无数据信息字体(如果需要)
plot.setNoDataMessageFont(new Font("SimSun", Font.PLAIN, 18));
折线图:
java
// 设置字体
chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));
chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));
CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.getDomainAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));
plot.getRangeAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));
plot.getDomainAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));
plot.getRangeAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));
完整代码:
java
package com.example.demo.uitls;
import lombok.extern.slf4j.Slf4j;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
/**
* ChartWithChineseExample : 图表生成工具类
*
* @author zyw
* @create 2024-06-25 16:20
*/
@Slf4j
@Component
public class ChartWithChineseExample {
// 柱状图临时文件名
public final static String BAR_CHART_FILE_NAME = "BAR_CHART.png";
// 饼图临时文件名
public final static String PIE_CHART_FILE_NAME = "PIE_CHART.png";
// 折线图临时文件名
public final static String LINE_CHART_FILE_NAME = "LINE_CHART.png";
public static InputStream lineChartGeneration(String title, String x, String y, DefaultCategoryDataset dataset) {
registerChineseFont();
JFreeChart chart = ChartFactory.createLineChart(
title, // 图表标题
x, // X轴标签
y, // Y轴标签
dataset, // 数据集
PlotOrientation.VERTICAL, // 图表方向
true, // 是否显示图例
true, // 是否生成工具提示
false // 是否生成URL链接
);
chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));
chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 14));
// 示例字体为宋体,常规,14号
Font axisLabelFont = new Font("SimSun", Font.PLAIN, 14);
// X轴
chart.getCategoryPlot().getDomainAxis().setLabelFont(axisLabelFont);
chart.getCategoryPlot().getDomainAxis().setTickLabelFont(axisLabelFont);
// Y轴
chart.getCategoryPlot().getRangeAxis().setLabelFont(axisLabelFont);
chart.getCategoryPlot().getRangeAxis().setTickLabelFont(axisLabelFont);
try {
// 将图表转换为字节数组
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BufferedImage chartImage = chart.createBufferedImage(400, 300); // 设置图像的宽高
ImageIO.write(chartImage, "png", outputStream);
byte[] chartBytes = outputStream.toByteArray();
// 将字节数组转换为InputStream
InputStream inputStream = new ByteArrayInputStream(chartBytes);
return inputStream;
} catch (IOException e) {
log.error("折线图图生成异常");
return null;
}
}
/**
* 饼图生成
*
* @param title 标题
* @param dataset 数据集
* @return
*/
public static InputStream pieChartGeneration(String title, DefaultPieDataset dataset) {
registerChineseFont();
// 使用数据集创建饼图
JFreeChart chart = ChartFactory.createPieChart3D(
title, // 图表标题
dataset, // 数据集
true, // 是否显示图例
true, // 是否生成工具提示
false // 是否生成URL链接
);
chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));
chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));
// 获取饼图的plot对象,以便进行进一步定制
PiePlot3D plot = (PiePlot3D) chart.getPlot();
// 设置标签字体
plot.setLabelFont(new Font("SimSun", Font.PLAIN, 14));
// 设置无数据信息字体(如果需要)
plot.setNoDataMessageFont(new Font("SimSun", Font.PLAIN, 18));
try {
// 将图表转换为字节数组
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BufferedImage chartImage = chart.createBufferedImage(400, 300); // 设置图像的宽高
ImageIO.write(chartImage, "png", outputStream);
byte[] chartBytes = outputStream.toByteArray();
// 将字节数组转换为InputStream
InputStream inputStream = new ByteArrayInputStream(chartBytes);
return inputStream;
} catch (IOException e) {
log.error("饼图生成异常");
return null;
}
}
/**
* 注册中文字体
*/
public static void registerChineseFont() {
// 注册中文字体(这里假设已经将字体文件放置在项目的resources目录下)
InputStream fontStream = ChartWithChineseExample.class.getResourceAsStream("/font/SIMSUN.TTC"); // 路径根据实际情况调整
try {
Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream).deriveFont(Font.PLAIN, 12);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(customFont);
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
}
/**
* 创建柱状图表
*
* @param dataset 数据集
* @return
*/
public static InputStream createChartPanel(String title, String x, String y, DefaultCategoryDataset dataset) {
registerChineseFont();
// 创建图表
JFreeChart chart = ChartFactory.createBarChart(
title, // 图表标题
x, // X轴标签
y, // Y轴标签
dataset,
PlotOrientation.VERTICAL,
true, // 是否显示图例
true, // 是否使用工具提示
false // 是否生成URL链接
);
// 设置字体
chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));
chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));
CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.getDomainAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));
plot.getRangeAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));
plot.getDomainAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));
plot.getRangeAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));
try {
// 将图表转换为字节数组
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BufferedImage chartImage = chart.createBufferedImage(400, 300); // 设置图像的宽高
ImageIO.write(chartImage, "png", outputStream);
byte[] chartBytes = outputStream.toByteArray();
// 将字节数组转换为InputStream
InputStream inputStream = new ByteArrayInputStream(chartBytes);
return inputStream;
} catch (IOException e) {
log.error("柱状图生成异常");
return null;
}
}
}
5 业务层 OfficeServicel
在word中遍历所有段落,找到需要插入图表的段落索引。
此处省略上诉已展示代码。
java
/**
* OfficeServiceImpl :
*
* @author zyw
* @create 2024-06-24 15:41
*/
@Service
@Slf4j
public class OfficeServiceImpl implements OfficeService {
private static final String HEADER_2_1 = "营养成分摄入比例";
private static final String HEADER_2_2 = "心率血氧检查";
private static final String HEADER_2_3 = "睡眠质量趋势";
@Override
public XWPFDocument getHealthReport(HealthReportQuery query) {
try {
FileInputStream fileInputStream = SpringUtils.convertInputStreamToFileInputStream(resourceLoader.getResource(PERSONAL_HEALTH_REPORT_TEMPLATE).getInputStream());
XWPFDocument xwpfDocument = new XWPFDocument(fileInputStream);
// 插入历史体重
int index5 = OfficeUtils.findParagraphIndexByText(xwpfDocument, HEADER_2_1);
insertChartOne(xwpfDocument, index5);
// 插入心率检查
int index6 = OfficeUtils.findParagraphIndexByText(xwpfDocument, HEADER_2_2);
insertChartTwo(xwpfDocument, index6);
// 插入睡眠质量趋势
int index7 = OfficeUtils.findParagraphIndexByText(xwpfDocument, HEADER_2_3);
insertChartThree(xwpfDocument, index7);
return xwpfDocument;
} catch (Exception e) {
log.info("获取健康报告失败", e);
return null;
}
}
/**
* 获取文本在文档中的索引
*
* @param doc 文档
* @param text 文本标识
* @return
*/
public static int findParagraphIndexByText(XWPFDocument doc, String text) {
// 获取所有段落
List<XWPFParagraph> paragraphs = doc.getParagraphs();
// 查找目标段落
int targetParagraphIndex = -1;
for (int i = 0; i < paragraphs.size(); i++) {
if (paragraphs.get(i).getText().contains(text)) {
targetParagraphIndex = i;
break;
}
}
return targetParagraphIndex;
}
/**
* 插入图表 1
*
* @param document
* @param index
* @throws Exception
*/
public void insertChartOne(XWPFDocument document, Integer index) throws Exception {
// 填充图表数据
DefaultPieDataset<String> dataset = new DefaultPieDataset<String>();
dataset.setValue("碳水化合物(30%)", 30);
dataset.setValue("蛋白质(30%)", 30);
dataset.setValue("脂肪(25%)", 25);
dataset.setValue("纤维等营养素(15%)", 15);
// 创建图表示例
InputStream chartPanel = ChartWithChineseExample.pieChartGeneration("营养成分摄入比例", dataset);
// 获取所有段落
List<XWPFParagraph> paragraphs = document.getParagraphs();
// 在目标段落后添加一个新的段落
XWPFParagraph paragraph = document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());
// 设置段落的样式和属性,实现换行
paragraph.setWordWrap(true); // 设置自动换行
// 设置段落水平居中
paragraph.setAlignment(ParagraphAlignment.CENTER);
// 设置段落内文字(这里是空格)垂直居中
paragraph.setVerticalAlignment(TextAlignment.CENTER);
// 调整行距以确保图片上下居中,这一步可能需要根据实际情况调整
XWPFRun run = paragraph.createRun();
run.addPicture(chartPanel, XWPFDocument.PICTURE_TYPE_PNG, ChartWithChineseExample.BAR_CHART_FILE_NAME, Units.toEMU(400), Units.toEMU(300));
}
/**
* 插入图表2 心率血氧
*
* @param document
* @param index
*/
public void insertChartTwo(XWPFDocument document, Integer index) throws Exception {
// 填充图表数据
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(77, "心率", "2024-06-23");
dataset.addValue(85, "心率", "2024-06-24");
dataset.addValue(99, "心率", "2024-06-25");
dataset.addValue(92.76, "血氧饱和度", "2024-06-23");
dataset.addValue(98.74, "血氧饱和度", "2024-06-24");
dataset.addValue(94.2, "血氧饱和度", "2024-06-25");
// 创建图表示例
InputStream chartPanel = ChartWithChineseExample.createChartPanel("心率和血氧饱和度图表", "日期", "心率(次/分)、血氧饱和度(%)", dataset);
// 获取所有段落
List<XWPFParagraph> paragraphs = document.getParagraphs();
// 在目标段落后添加一个新的段落
XWPFParagraph paragraph = document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());
// 设置段落的样式和属性,实现换行
paragraph.setWordWrap(true); // 设置自动换行
// 设置段落水平居中
paragraph.setAlignment(ParagraphAlignment.CENTER);
// 设置段落内文字(这里是空格)垂直居中
paragraph.setVerticalAlignment(TextAlignment.CENTER);
// 调整行距以确保图片上下居中,这一步可能需要根据实际情况调整
XWPFRun run = paragraph.createRun();
run.addPicture(chartPanel, XWPFDocument.PICTURE_TYPE_PNG, ChartWithChineseExample.PIE_CHART_FILE_NAME, Units.toEMU(400), Units.toEMU(300));
}
/**
* 插入图表3 睡眠质量趋势
*
* @param document
* @param index
* @throws Exception
*/
public void insertChartThree(XWPFDocument document, Integer index) throws Exception {
// 填充图表数据
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(7.8, "起床时间", "06/18");
dataset.addValue(8, "起床时间", "06/19");
dataset.addValue(7.5, "起床时间", "06/20");
dataset.addValue(8.3, "起床时间", "06/21");
dataset.addValue(9, "起床时间", "06/22");
dataset.addValue(9.5, "起床时间", "06/23");
dataset.addValue(23, "睡眠时间", "06/18");
dataset.addValue(24, "睡眠时间", "06/19");
dataset.addValue(22.6, "睡眠时间", "06/20");
dataset.addValue(23.2, "睡眠时间", "06/21");
dataset.addValue(21.8, "睡眠时间", "06/22");
dataset.addValue(23.7, "睡眠时间", "06/23");
// 创建图表示例
InputStream chartPanel = ChartWithChineseExample.lineChartGeneration("睡眠质量趋势", "日期", "睡眠时间", dataset);
// 获取所有段落
List<XWPFParagraph> paragraphs = document.getParagraphs();
// 在目标段落后添加一个新的段落
XWPFParagraph paragraph = OfficeUtils.insertNewParagraph(paragraphs, document,index);
// 设置段落的样式和属性,实现换行
paragraph.setWordWrap(true); // 设置自动换行
// 设置段落水平居中
paragraph.setAlignment(ParagraphAlignment.CENTER);
// 设置段落内文字(这里是空格)垂直居中
paragraph.setVerticalAlignment(TextAlignment.CENTER);
// 调整行距以确保图片上下居中,这一步可能需要根据实际情况调整
XWPFRun run = paragraph.createRun();
run.addPicture(chartPanel, XWPFDocument.PICTURE_TYPE_PNG, ChartWithChineseExample.LINE_CHART_FILE_NAME, Units.toEMU(400), Units.toEMU(300));
}
}
6 通用工具类 OfficeUtils
java
package com.example.demo.uitls;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.lang.reflect.Field;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
/**
* OfficeUtils : Office工具类
*
* @author zyw
* @create 2024-06-24 16:35
*/
public class OfficeUtils {
/**
* 插入新段落
*
* @param paragraphs 段落集合
* @param document 文档
* @param index 插入位置
* @return 新段落
*/
public static XWPFParagraph insertNewParagraph(List<XWPFParagraph> paragraphs, XWPFDocument document, Integer index) {
if (paragraphs.size() == index + 1) {
return document.createParagraph();
} else {
return document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());
}
}
/**
* 设置表格宽度去除边框
*
* @param table 表格
* @param width 宽度值
*/
public static void setTableWidthToRemoveBorder(XWPFTable table, Integer width) {
// 去除表格边框
CTTblPr tblPr2 = table.getCTTbl().getTblPr();
CTTblBorders borders2 = tblPr2.addNewTblBorders();
borders2.addNewBottom().setVal(STBorder.NONE);
borders2.addNewTop().setVal(STBorder.NONE);
borders2.addNewLeft().setVal(STBorder.NONE);
borders2.addNewRight().setVal(STBorder.NONE);
borders2.addNewInsideH().setVal(STBorder.NONE);
borders2.addNewInsideV().setVal(STBorder.NONE);
// 设置表格整体样式
tblPr2.addNewTblW().setW(BigInteger.valueOf(width)); // 设置表格宽度
}
/**
* 设置表格单元格宽度及文本居中
*
* @param cell 单元格
* @param width 宽度占比
*/
public static void setTheLandscapeHeader(XWPFTableCell cell, double width) {
setsTheCellWidth(cell, width);
// 获取单元格属性对象
CTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();
// 设置垂直对齐方式为居中
CTVerticalJc vJc = tcPr.isSetVAlign() ? tcPr.getVAlign() : tcPr.addNewVAlign();
vJc.setVal(STVerticalJc.CENTER);
}
/**
* 设置表格单元格宽度样式靠左
*
* @param cell 单元格
* @param width 宽度占比
*/
public static void setsTheCellWidthLeft(XWPFTableCell cell, double width) {
// 假设A4纸宽约为210mm,1mm=360EMU,则A4宽约为7920EMU
int emuFor30Percent = (int) (7920 * width);
CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
// 设置宽度为2000EMU,你可以根据需要调整这个值
ctTblWidth.setW(BigInteger.valueOf(emuFor30Percent));
// 设置宽度类型为字符单位(也可以是其他单位,如百分比等)
ctTblWidth.setType(STTblWidth.PCT);
// 设置垂直居中
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
for (XWPFParagraph para : cell.getParagraphs()) {
//居中
para.setAlignment(ParagraphAlignment.LEFT);
}
}
/**
* 设置表格单元格宽度
*
* @param cell 单元格
* @param width 宽度占比
*/
public static void setsTheCellWidth(XWPFTableCell cell, double width) {
// 假设A4纸宽约为210mm,1mm=360EMU,则A4宽约为7920EMU
int emuFor30Percent = (int) (7920 * width);
CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();
// 设置宽度为2000EMU,你可以根据需要调整这个值
ctTblWidth.setW(BigInteger.valueOf(emuFor30Percent));
// 设置宽度类型为字符单位(也可以是其他单位,如百分比等)
ctTblWidth.setType(STTblWidth.PCT);
// 设置垂直居中
cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
for (XWPFParagraph para : cell.getParagraphs()) {
//居中
para.setAlignment(ParagraphAlignment.CENTER);
}
}
/**
* 设置表格行的高度
*
* @param row 行
* @param heightCm 高度占比
*/
public static void setRowHeight(XWPFTableRow row, double heightCm) {
int emuForHeight = (int) (360 * heightCm);
CTTrPr trPr = row.getCtRow().addNewTrPr();
CTHeight ht = trPr.addNewTrHeight();
ht.setVal(BigInteger.valueOf(emuForHeight));
}
/**
* 创建表格行
*
* @param table 表格
* @param index 行索引
* @return
*/
public static XWPFTableRow createRow(XWPFTable table, int index) {
return Objects.isNull(table.getRow(index)) ? table.createRow() : table.getRow(index);
}
/**
* 创建单元格
*
* @param row 行
* @param index 列索引
* @return
*/
public static XWPFTableCell createCell(XWPFTableRow row, int index) {
return Objects.isNull(row.getCell(index)) ? row.createCell() : row.getCell(index);
}
/**
* 获取文本在文档中的索引
*
* @param doc 文档
* @param text 文本标识
* @return
*/
public static int findParagraphIndexByText(XWPFDocument doc, String text) {
// 获取所有段落
List<XWPFParagraph> paragraphs = doc.getParagraphs();
// 查找目标段落
int targetParagraphIndex = -1;
for (int i = 0; i < paragraphs.size(); i++) {
if (paragraphs.get(i).getText().contains(text)) {
targetParagraphIndex = i;
break;
}
}
return targetParagraphIndex;
}
/**
* 对象转Map
*
* @param obj 对象
* @return
*/
public static Map<String, String> objectToMap(Object obj) {
Map<String, String> map = new HashMap<>();
Class<?> clazz = obj.getClass();
// 获取类中所有声明的字段(包括私有、受保护、默认、公共)
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true); // 设置字段可访问(如果是私有的)
try {
Object value = field.get(obj);
String key = "${" + field.getName() + "}"; // 构造key,以${name}形式
map.put(key, String.valueOf(value));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
/**
* 段落文本填充
*
* @param document 文档
* @param insertTextMap 填充内容
*/
public static void paragraphTextFilling(XWPFDocument document, Map<String, String> insertTextMap) {
Set<String> set = insertTextMap.keySet();
Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();
while (itPara.hasNext()) {
// 获取文档中当前的段落文字信息
XWPFParagraph paragraph = itPara.next();
List<XWPFRun> run = paragraph.getRuns();
// 遍历段落文字对象
for (int i = 0; i < run.size(); i++) {
// 获取段落对象
if (run.get(i) == null) { //段落为空跳过
continue;
}
String sectionItem = null;
try {
// 检查段落中是否包含文本框
sectionItem = run.get(i).getText(run.get(i).getTextPosition()); //段落内容
} catch (Exception e) {
}
if (sectionItem == null) {
continue;
}
// 遍历自定义表单关键字,替换Word文档中的内容
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
// 当前关键字
String key = iterator.next();
// 替换内容
sectionItem = sectionItem.replace(key, String.valueOf(insertTextMap.get(key)));
}
run.get(i).setText(sectionItem, 0);
}
}
}
/**
* 处理Word响应
*
* @param downloadName 下载文件名
* @param inputStream 文件输入流
* @param response 响应
*/
public static void processingWordResponses(String downloadName,
InputStream inputStream,
HttpServletResponse response) {
try {
// 设置响应的Content-Type
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
// 设置Content-Disposition头部,指示浏览器下载文件,文件名为document.docx
downloadName = new String(downloadName.getBytes("UTF-8"), "ISO-8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + downloadName + ".docx");
// 获取响应的输出流
OutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[4096];
int bytesRead = -1;
// 将InputStream中的内容写入到OutputStream中
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
// 关闭流
inputStream.close();
outputStream.close();
} catch (Exception e) {
}
}
/**
* word转InputStream
*
* @param document
* @return
*/
public static InputStream writeDocumentToInputStream(XWPFDocument document) {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
document.write(byteArrayOutputStream);
byteArrayOutputStream.close();
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
7 控制层 OfficeController
java
package com.example.demo.controller;
import com.example.demo.dto.HealthReportQuery;
import com.example.demo.service.OfficeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;
/**
* OfficeController : Office办公文件控制器
*
* @author zyw
* @create 2024-06-24 15:40
*/
@Tag(name = "Office办公文件控制器")
@RestController
@RequestMapping("/office")
public class OfficeController {
@Resource
private OfficeService officeService;
@GetMapping("/getHealthReportWord")
@Operation(summary = "获取健康报告Word", description = "获取健康报告")
@Parameters({
@Parameter(name = "name", description = "姓名", required = true, in = ParameterIn.QUERY),
@Parameter(name = "gender", description = "性别", required = true, in = ParameterIn.QUERY),
@Parameter(name = "age", description = "年龄", required = true, in = ParameterIn.QUERY)
})
public void getHealthReportWord(HealthReportQuery query, HttpServletResponse response) {
officeService.getHealthReportWord(officeService.getHealthReport(query), query, response);
}
}
8 导出效果
Word效果:
PDF效果: