JFreeChart 生成Word图表

文章目录

  • 1 思路
    • 1.1 概述
    • 1.2 支持的图表类型
    • 1.3 特性
  • 2 准备模板
  • 3 导入依赖
  • 4 图表生成工具类 ChartWithChineseExample
    • 步骤 1: 准备字体文件
    • 步骤 2: 注册字体到`FontFactory`
    • 步骤 3: 设置图表具体位置的字体
      • 柱状图:
      • 饼图:
      • 折线图:
      • 完整代码:
  • 5 业务层 OfficeServicel
  • 6 通用工具类 OfficeUtils
  • 7 控制层 OfficeController
  • 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 导入依赖

        <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这个字体文件,可以这样做:

    /*** 注册中文字体*/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: 设置图表具体位置的字体

柱状图:

        // 示例字体为宋体,常规,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);

饼图:

        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));

折线图:

        // 设置字体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));

完整代码:

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();// 将字节数组转换为InputStreamInputStream 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();// 将字节数组转换为InputStreamInputStream 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();// 将字节数组转换为InputStreamInputStream inputStream = new ByteArrayInputStream(chartBytes);return inputStream;} catch (IOException e) {log.error("柱状图生成异常");return null;}}}

5 业务层 OfficeServicel

在word中遍历所有段落,找到需要插入图表的段落索引。

此处省略上诉已展示代码。

/*** 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 = "睡眠质量趋势";@Overridepublic 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

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宽约为7920EMUint 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宽约为7920EMUint 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-Typeresponse.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");// 设置Content-Disposition头部,指示浏览器下载文件,文件名为document.docxdownloadName = 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

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 {@Resourceprivate 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效果:
在这里插入图片描述

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

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

相关文章

Quectel EM05-CE 模块测试

作者简介&#xff1a; 一个平凡而乐于分享的小比特&#xff0c;中南民族大学通信工程专业研究生在读&#xff0c;研究方向无线联邦学习 擅长领域&#xff1a;驱动开发&#xff0c;嵌入式软件开发&#xff0c;BSP开发 作者主页&#xff1a;一个平凡而乐于分享的小比特的个人主页…

AI绘画:探索人工智能与艺术的奇妙结合

前言 人工智能技术的不断发展&#xff0c;使得AI绘画逐渐成为艺术领域的新宠。AI绘画是指利用人工智能算法进行绘画创作的一种艺术形式&#xff0c;它可以模拟人类艺术家的创作过程&#xff0c;创造出各种独特的艺术作品。 突破传统艺术的极限&#xff1a;AI绘画的无限可能性 …

每日一题---OJ题:分隔链表

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;今天我们一起来看看这道题----分隔链表 emmmm&#xff0c;这道题&#xff0c;看描述应该不算太难&#xff0c;我们一起来画一画图呗&#xff01; 题目读懂了&#xff0c;那么如何破解这道题呢&#xff1f; 思路&…

探索PcapPlusPlus开源库:网络数据包处理与性能优化

文章目录 0. 本文概要1. PcapPlusPlus介绍1.1 概述1.2主要特性和功能1.3 PcapPlusPlus 主要模块关系和依赖1.4 网络协议层处理过程 2. 实例2.1 基于 PcapPlusPlus 的应用程序设计和封装流程&#xff1a;2.2 多线程示例代码2.3 代码说明&#xff1a; 3. 程序性能进一步优化3.1 避…

linux虚拟机部署的MySQL如何使用外网访问?教你轻松使用cpolar在centos搭建内网穿透

文章目录 写在前面实现Linux的内网穿透1、官网账号注册2、在Linux部署我们自己的项目3、一键自动下载安装cpolar4、设置自己的token5、启动cpolar服务6、MySQL穿透测试 卸载方法 写在前面 相信很多小伙伴在本地搭建了一个MySQL数据库&#xff0c;想让其他同事或者合作者一起使…

Shell编程实战

脚本编程步骤 脚本编程一般分为以下几个步骤: 需求分析:根据系统管理的需求&#xff0c;分析脚本要实现的功能、功能实现的层次、实现的命令与语句等; 命令测试:将要用到的命令逐个进行测试&#xff0c;以决定使用的选项、要设置的变量等: 脚本编程:将测试好的命令写入到脚本文…

python实现网页自动化(自动登录需要验证的网页)

引言: python作为实现网页自动化的一个重要工具,其强大的各种封装的库使得程序运行更加简洁,只需要下载相应的库,然后调用库中的函数就可以简便的实现我们想要的网页相关操作。 正文: 我的前几篇文章写了关于初学爬虫中比较容易上手的功能,例如爬取静态网页的数据、动…

Java并发编程基础知识点

目录 Java并发编程基础知识点1、线程&#xff0c;进程概念及二者的关系进程相关概念线程相关概念进程与线程的关系补充小知识点&#xff1a; 2、线程的状态Java线程的状态&#xff1a;Java线程不同状态之间的切换图示 3、Java程序中如何创建线程&#xff1f;①、继承Thread类②…

使用Python绘制极坐标图

使用Python绘制极坐标图 极坐标图极坐标图的优点使用场景 效果代码 极坐标图 极坐标图&#xff08;Polar Chart&#xff09;是一种图表类型&#xff0c;用于显示在极坐标系中的数据。极坐标图使用圆形坐标系&#xff0c;角度表示一个变量的值&#xff0c;半径表示另一个变量的…

elk对于集群实例的日志的整合-基于logstash采集日志

说明&#xff1a;基于logstash采集日志 环境&#xff1a; 物理机192.168.31.151 一.启动2个测试实例&#xff0c;每5-10s随机生成一条订单日志 实例一 包位置&#xff1a;/home/logtest/one/log-test-0.0.1-SNAPSHOT.jar 日志位置:/docker/elastic/logstash_ingest_data/l…

“医”路赋能!实在智能签约新疆百草堂

在新疆&#xff0c;地域广阔、人口稀疏的特性限制了实体药店的服务覆盖面&#xff0c;同时《“健康中国2030”规划纲要》对医药电商模式的规范化要求&#xff0c;使得医药电商迎来前所未有的增长机遇&#xff0c;而大量的订单也带来了运营成本上升、服务时效性低的挑战。 近日&…

携程二面测开—中核

4.12 35min面试经验 自我介绍 在面试的开始&#xff0c;我简洁明了地进行了自我介绍&#xff0c;突出了我的教育背景、技能特长以及实习经历&#xff0c;为后续的面试内容打下了良好的基础。 实习的具体工作内容 在谈及实习经历时&#xff0c;我详细阐述了在实习期间所承担…

[Go 微服务] Kratos 验证码业务

文章目录 1.环境准备2.验证码服务2.1 kratos 初始化验证码服务项目2.2 使用 Protobuf 定义验证码生成接口2.3 业务逻辑代码实现 1.环境准备 protoc和protoc-gen-go插件安装和kratos工具安装 protoc下载 下载二进制文件&#xff1a;https://github.com/protocolbuffers/protobu…

【LeetCode】 740. 删除并获得点数

这真是一道好题&#xff01;这道题不仅考察了抽象思维&#xff0c;还考察了分析能力、化繁为简的能力&#xff0c;同时还有对基本功的考察。想顺利地做出这道题还挺不容易&#xff01;我倒在了第一步与第二步&#xff1a;抽象思维和化繁为简。题目的要求稍微复杂一些&#xff0…

CSS 背景添加白色小圆点样式

css也是开发过程中不可忽视的技巧 此专栏用来纪录不常见优化页面样式的css代码 效果图: 未添加之前: 代码: background: radial-gradient(circle at 1px 1px, #3d3c3c 2px, transparent 0);background-size: 20px 25px;

【Docker项目实战篇】Docker部署PDF多功能工具Stirling-PDF

【Docker项目实战篇】Docker部署PDF多功能工具Stirling-PDF 前言一、Stirling-PDF介绍1.1 Stirling-PDF简介1.2 Stirling-PDF功能 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四…

鸿蒙如何打包应用程序

总结鸿蒙应用程序包 之前文章详细讲解了关于三种程序包的内容&#xff0c;现在简单总结一下&#xff1a; 1. 总结 首先需要搞清楚鸿蒙项目的模块Module的分类: Module分为“Ability”和“Library”两种类型 HAP HAP: Harmony Ability Package , 叫做鸿蒙Ability包。 “Abil…

【AI大模型】跌倒监控与健康:技术实践及如何改变未来

文章目录 1. **背景与意义**2. **关键技术与方法**2.1 传感器数据融合2.2 深度学习模型2.3 行为模式识别2.4 预测与预防 3. **应用场景**3.1 老年人跌倒预警3.2 康复患者监测3.3 高风险职业防护 4. **实践案例**案例1&#xff1a;某老年社区的跌倒预警系统案例2&#xff1a;康复…

C++ 几何算法 打印图案 1*2*5*6 –3*4(Geometric Algorithms Print the pattern 1*2*5*6 –3*4)

给定整数 N&#xff0c;任务是打印一个倒三角形&#xff0c;其中左半部分由 [1, N*(N1)/2] 范围内的元素组成&#xff0c;右半部分由 [N*(N1)/2 1, N*(N1)] 范围内的元素组成。 例子&#xff1a; 输入&#xff1a; N 3 输出&#xff1a; 1*2*3*10*11*12 4*5*8*9 …

业务模型扩展字段存储

构建业务模型时&#xff0c;通常模型会设置扩展信息&#xff0c;存储上一般使用JSON格式存储到db中。JSON虽然有较好的扩展性&#xff0c;但并没有结构化存储的类型和非空等约束&#xff0c;且强依赖代码中写入/读取时进行序列化/反序列化操作&#xff0c; 当扩展信息结构简单且…