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.pswp.cn/diannao/35887.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/35887.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/35887.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【QT】Svg圖標

目錄 SVGQT繪制SVG流程 SVG 一般而言&#xff0c;QSS是無法直接使用svg圖像的。 那如何才能顯示svg呢&#xff1f;我們知道svg的好處有很多&#xff0c;如矢量圖&#xff0c;體積小等等 svg本來就是一個document&#xff08;可參考12&#xff09;&#xff0c;QT提供了QSvgRend…

二叉樹深度優先搜索(非遞歸實現,迭代法)

目錄 為什么可以用迭代法實現二叉樹的前后中序遍歷&#xff1f; 前序遍歷 后序遍歷 中序遍歷 為什么可以用迭代法實現二叉樹的前后中序遍歷&#xff1f; 因為遞歸的實現本質是&#xff0c;每次遞歸調用都會把函數的局部變量、參數值和返回地址等壓入調用棧中&#xff0c;然…

web期末作業設計網頁

設計一個網頁作為期末作業是一個很好的機會來展示你的前端開發技能。以下是一些步驟和建議&#xff0c;幫助你完成這個項目&#xff1a; 1. 確定網頁主題和目的 決定你的網頁是關于什么的&#xff08;例如&#xff1a;個人博客、在線商店、公司網站、信息發布平臺等&#xff…

國產車規MCU OTA方案總結

目錄 1. 旗芯微FC4150 OTA 2. 云途YTM32B1MD OTA 3.小結 今天沒有廢話&#xff0c;啪一下很快&#xff0c;把目前接觸到的國內帶eFlash的車規MCU硬件OTA方案做一個梳理。 1. 旗芯微FC4150 OTA 旗芯微FC4150是基于ARM Cortex(快去審核下官網介紹&#xff0c;少了個T)-M4F內…

入門者必看-Ansible:自動化運維的利器

1. 引言 在當今快速變化的IT環境中&#xff0c;自動化成為了提升工作效率和確保系統一致性的重要手段。Ansible作為一個開源的自動化工具&#xff0c;因其簡單易用、功能強大而廣受歡迎。本文將深入探討Ansible的概念、架構、體系結構、搭建過程、常用操作方式以及使用場景&…

openGauss Developer Day 2024丨MogDB實現數據庫技術跨越,Ustore引擎革新存儲新境界

openGauss Developer Day 2024 6月21日&#xff0c;openGauss Developer Day 2024在北京昆泰嘉瑞文化中心成功召開。大會聚集學術專家、行業用戶、合作伙伴和開發者&#xff0c;共同探討數據庫面向多場景的技術創新&#xff0c;分享基于 openGauss 的行業聯合創新成果及實踐案例…

探索PHP中的魔術常量

PHP中的魔術常量&#xff08;Magic Constants&#xff09;是一些特殊的預定義常量&#xff0c;它們在不同的上下文中具有不同的值。這些常量可以幫助開發者獲取文件路徑、行號、函數名等信息&#xff0c;從而方便調試和日志記錄。本文將詳細介紹PHP中的魔術常量&#xff0c;幫助…

web前端——javaScript

目錄 一、javaScript概述 1.javaScript歷史 2.JavaScript與html,css關系 二、基本語法 ①放在head中 ②放在 body中 ③寫在外部的.js文件中 1.變量 2.數據類型 3.算術運算符 4.邏輯運算符 5.賦值運算 6.邏輯運算符 7.條件運算符 8.控制語句 三、函數 1…

智能掃地機器人環境感知與地圖構建優化方案

以下是一個針對智能掃地機器人程序中環境感知與地圖構建問題的具體解決方案&#xff0c;參考了之前文章中的相關技術和信息&#xff1a; 智能掃地機器人環境感知與地圖構建優化方案 一、引入高精度傳感器 激光雷達&#xff08;LiDAR&#xff09;&#xff1a;使用高精度激光雷達…

模板語法輪播

1.常用的視圖容器組件 view類似于div進行使用 <div></div><view></view> scroll-view實現滾動列表效果 <scroll-view scroll-y> <view></view> <view></view> <view></view> </scroll-view> …

數據庫死鎖解決

一、Oracle死鎖查看和解決辦法匯總 由于生產的tomcat 經常有假死問題&#xff0c;困擾很久&#xff0c;最后發現有死鎖&#xff0c;解決辦法分享 1.1、查看死鎖 1.1.1、用dba用戶執行以下語句 select username,lockwait,status,machine,program from v$session where sid in …

Arduino - 按鈕 - 長按短按

Arduino - Button - Long Press Short Press Arduino - 按鈕 - 長按短按 Arduino - Button - Long Press Short Press We will learn: 我們將學習&#xff1a; How to detect the button’s short press 如何檢測按鈕的短按How to detect the button’s long press 如何檢測…

重大進展!微信支付收款碼全場景接入銀聯網絡

據中國銀聯6月19日消息&#xff0c;近日&#xff0c;銀聯網絡迎來微信支付收款碼場景的全面接入&#xff0c;推動條碼支付互聯互通取得新進展&#xff0c;為境內外廣大消費者提供更多支付選擇、更好支付體驗。 2024年6月&#xff0c;伴隨微信支付經營收款碼的開放&#xff0c;微…

Docker部署Nginx+Keepalived

# 創建掛載路徑 mkdir /data/nginx_keep/nginx/conf -p mkdir /data/nginx_keep/keepalived/vim nginx.conf user nginx; worker_processes auto;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;events {worker_connections 1024; }http {incl…

Rust: duckdb和polars讀csv文件比較

一、文件準備 樣本內容&#xff0c;N行9列的csv標準格式&#xff0c;有字符串&#xff0c;有浮點數&#xff0c;有整型。 有兩個csv文件&#xff0c;一個大約是2.1萬行&#xff1b;一個是64萬行。 二、toml文件 [package] name "my_duckdb" version "0.1.0&…

opencv簡單小項目

OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一個開源的計算機視覺和機器學習軟件庫&#xff0c;它提供了大量的圖像和視頻處理功能。使用OpenCV可以開發各種簡單的小項目&#xff0c;例如&#xff1a; 圖像基本操作&#xff1a; 讀取和顯示圖像。調整…

弱監督學習

弱監督學習&#xff08;Weak Supervision&#xff09;是一種利用不完全、不精確或噪聲數據進行模型訓練的方法。以下是一些常用的弱監督方法及其原理&#xff1a; 1. 數據增強&#xff08;Data Augmentation&#xff09; 原理&#xff1a; 數據增強是一種通過增加訓練數據的多…

區塊鏈的歷史和發展:從比特幣到以太坊

想象一下&#xff0c;你住在一個小鎮上&#xff0c;每個人都有一個大賬本&#xff0c;記錄著所有的交易。這個賬本很神奇&#xff0c;每當有人買賣東西&#xff0c;大家都會在自己的賬本上記一筆&#xff0c;確保每個人的賬本都是一致的。這就是區塊鏈的基本思想。而區塊鏈的故…

HG/T 5838-2021金屬骨架發泡橡膠復合密封板檢測

金屬骨架發泡橡膠復合密封板是指工作溫度范圍-40&#xff5e;140℃&#xff0c;峰值溫度為150℃條件下使用的金屬骨架發泡密封板。 HG/T 5838-2021金屬骨架發泡橡膠復合密封板檢測項目&#xff1a; 測試項目 測試標準 外觀 HG/T 5838 厚度 HG/T 5838 壓縮性能 GB/T 206…

VSCode安裝OpenImageDebugger

VSCode安裝OpenImageDebugger 1. 官網2. 編譯2.1 依賴項2.2 編譯 OpenImageDebugger2.3 配置 GDB 和 LLDB 3. 驗證安裝是否成功 1. 官網 下載路徑&#xff1a;OpenImageDebugger 2. 編譯 2.1 依賴項 官網上描述&#xff0c; Qt 5.15.1Python 3.10.12 這兩個其實配置并不需…