word导出图表

package com.bjs.glasses.controller.test;

import org.apache.poi.util.Units;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;

import java.io.FileOutputStream;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DynamicWordExporter {

    public static void exportCombinedReport(String outputPath) {
        try (XWPFDocument document = new XWPFDocument()) {

            // 添加文档标题
            addDocumentTitle(document);

            // 添加概述
            addOverview(document);

            // 添加折线图 - 近视趋势分析
            addLineChart(document);

            // 添加柱状图 - 各年级近视分布
            addBarChart(document);

            // 添加饼图 - 近视程度分布
            addPieChart(document);

            // 添加数据表格
            addDataTable(document);

            // 添加总结
            addConclusion(document);

            // 保存文档
            try (FileOutputStream fos = new FileOutputStream(outputPath)) {
                document.write(fos);
                System.out.println("综合视力分析报告导出成功: " + outputPath);
            }

        } catch (Exception e) {
            System.err.println("导出失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private static void addDocumentTitle(XWPFDocument document) {
        // 主标题
        XWPFParagraph titlePara = document.createParagraph();
        titlePara.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun titleRun = titlePara.createRun();
        titleRun.setText("学生视力筛查综合分析报告");
        titleRun.setBold(true);
        titleRun.setFontSize(18);
        titleRun.setFontFamily("宋体");

        // 副标题
        XWPFParagraph subTitlePara = document.createParagraph();
        subTitlePara.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun subTitleRun = subTitlePara.createRun();
        subTitleRun.setText("2024年度视力健康数据分析");
        subTitleRun.setFontSize(14);
        subTitleRun.setItalic(true);

        // 报告日期
        XWPFParagraph datePara = document.createParagraph();
        datePara.setAlignment(ParagraphAlignment.RIGHT);
        XWPFRun dateRun = datePara.createRun();
        dateRun.setText("报告生成时间: " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        dateRun.setFontSize(10);
        dateRun.setColor("666666");

        document.createParagraph(); // 空行
    }

    private static void addOverview(XWPFDocument document) {
        XWPFParagraph overviewPara = document.createParagraph();
        XWPFRun overviewRun = overviewPara.createRun();
        overviewRun.setText("报告概述:");
        overviewRun.setBold(true);
        overviewRun.setFontSize(12);

        XWPFParagraph contentPara = document.createParagraph();
        XWPFRun contentRun = contentPara.createRun();
        contentRun.setText("本报告基于2022-2024年度全校视力筛查数据,全面分析学生视力状况变化趋势。"
                + "通过折线图展示近视率年度变化,柱状图展示各年级分布情况,饼图展示近视程度构成。"
                + "数据显示近视率呈逐年上升趋势,需加强视力保护措施。");
        contentRun.setFontSize(11);

        document.createParagraph();
    }

    private static void addLineChart(XWPFDocument document) {
        try {
            // 图表标题
            addChartTitle(document, "1. 近三年近视趋势分析");

            // 创建图表段落
            XWPFParagraph chartPara = document.createParagraph();
            chartPara.setAlignment(ParagraphAlignment.CENTER);

            // 创建chart图表对象
            XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);

            // 图表相关设置
            chart.setTitleText("近三年各年级近视率趋势"); // 图表标题
            chart.setTitleOverlay(false); // 图例是否覆盖标题

            // 图例设置
            XDDFChartLegend legend = chart.getOrAddLegend();
            legend.setPosition(LegendPosition.TOP); // 图例位置

            // X轴(分类轴)相关设置
            XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); // 创建X轴,并且指定位置
            xAxis.setTitle("年份"); // x轴标题
            String[] xAxisData = new String[] {"2022", "2023", "2024"};
            XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData); // 设置X轴数据

            // Y轴(值轴)相关设置
            XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT); // 创建Y轴,指定位置
            yAxis.setTitle("近视率(%)"); // Y轴标题
            yAxis.setMinimum(0.0);
            yAxis.setMaximum(50.0);

            // 创建折线图对象
            XDDFLineChartData lineChart = (XDDFLineChartData) chart.createData(ChartTypes.LINE, xAxis, yAxis);

            // 各年级近视率数据
            Double[] grade1Data = new Double[]{25.0, 27.5, 29.0}; // 一年级
            Double[] grade2Data = new Double[]{28.0, 30.5, 32.0}; // 二年级
            Double[] grade3Data = new Double[]{32.5, 35.0, 37.5}; // 三年级
            Double[] grade4Data = new Double[]{36.0, 38.5, 41.0}; // 四年级
            Double[] grade5Data = new Double[]{40.5, 43.0, 45.5}; // 五年级
            Double[] grade6Data = new Double[]{45.0, 47.5, 50.0}; // 六年级

            // 加载折线图数据集 - 多个系列
            addLineSeries(lineChart, xAxisSource, XDDFDataSourcesFactory.fromArray(grade1Data), "一年级", MarkerStyle.CIRCLE);
            addLineSeries(lineChart, xAxisSource, XDDFDataSourcesFactory.fromArray(grade2Data), "二年级", MarkerStyle.SQUARE);
            addLineSeries(lineChart, xAxisSource, XDDFDataSourcesFactory.fromArray(grade3Data), "三年级", MarkerStyle.DIAMOND);
            addLineSeries(lineChart, xAxisSource, XDDFDataSourcesFactory.fromArray(grade4Data), "四年级", MarkerStyle.TRIANGLE);
            addLineSeries(lineChart, xAxisSource, XDDFDataSourcesFactory.fromArray(grade5Data), "五年级", MarkerStyle.STAR);
            addLineSeries(lineChart, xAxisSource, XDDFDataSourcesFactory.fromArray(grade6Data), "六年级", MarkerStyle.DOT);

            // 绘制折线图
            chart.plot(lineChart);

            document.createParagraph(); // 空行

        } catch (Exception e) {
            System.err.println("创建折线图时出错: " + e.getMessage());
            addChartPlaceholder(document, "折线图 - 近视趋势分析");
        }
    }

    private static void addBarChart(XWPFDocument document) {
        try {
            // 图表标题
            addChartTitle(document, "2. 各年级近视人数分布");

            // 创建图表段落
            XWPFParagraph chartPara = document.createParagraph();
            chartPara.setAlignment(ParagraphAlignment.CENTER);

            // 创建chart图表对象
            XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);

            // 图表相关设置
            chart.setTitleText("2024年各年级近视程度分布"); // 图表标题
            chart.setTitleOverlay(false); // 图例是否覆盖标题

            // 图例设置
            XDDFChartLegend legend = chart.getOrAddLegend();
            legend.setPosition(LegendPosition.TOP); // 图例位置

            // X轴(分类轴)相关设置
            XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); // 创建X轴,并且指定位置
            xAxis.setTitle("年级"); // x轴标题
            String[] xAxisData = new String[] {"一年级", "二年级", "三年级", "四年级", "五年级", "六年级"};
            XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData); // 设置X轴数据

            // Y轴(值轴)相关设置
            XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT); // 创建Y轴,指定位置
            yAxis.setTitle("人数"); // Y轴标题
            yAxis.setCrossBetween(AxisCrossBetween.BETWEEN); // 设置图柱的位置:BETWEEN居中

            // 创建柱状图对象
            XDDFBarChartData barChart = (XDDFBarChartData) chart.createData(ChartTypes.BAR, xAxis, yAxis);
            barChart.setBarDirection(BarDirection.COL); // 设置柱状图的方向:COL竖向

            // 各年级不同近视程度数据
            Double[] mildData = new Double[]{25.0, 32.0, 45.0, 58.0, 67.0, 75.0};    // 轻度近视
            Double[] moderateData = new Double[]{12.0, 18.0, 28.0, 35.0, 42.0, 48.0}; // 中度近视
            Double[] highData = new Double[]{5.0, 8.0, 12.0, 18.0, 25.0, 32.0};       // 高度近视

            // 加载柱状图数据集 - 多个系列
            addBarSeries(barChart, xAxisSource, XDDFDataSourcesFactory.fromArray(mildData), "轻度近视");
            addBarSeries(barChart, xAxisSource, XDDFDataSourcesFactory.fromArray(moderateData), "中度近视");
            addBarSeries(barChart, xAxisSource, XDDFDataSourcesFactory.fromArray(highData), "高度近视");

            // 绘制柱状图
            chart.plot(barChart);

            document.createParagraph(); // 空行

        } catch (Exception e) {
            System.err.println("创建柱状图时出错: " + e.getMessage());
            addChartPlaceholder(document, "柱状图 - 各年级近视分布");
        }
    }

    private static void addPieChart(XWPFDocument document) {
        try {
            // 图表标题
            addChartTitle(document, "3. 近视程度分布");

            // 创建图表段落
            XWPFParagraph chartPara = document.createParagraph();
            chartPara.setAlignment(ParagraphAlignment.CENTER);

            // 创建chart图表对象
            XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);

            // 图表相关设置
            chart.setTitleText("2024年近视程度构成"); // 图表标题
            chart.setTitleOverlay(false); // 图例是否覆盖标题

            // 图例设置
            XDDFChartLegend legend = chart.getOrAddLegend();
            legend.setPosition(LegendPosition.RIGHT); // 图例位置

            // 分类数据:饼图中的图例显示
            String[] categoryData = new String[] {"正常视力", "轻度近视", "中度近视", "高度近视"};
            XDDFCategoryDataSource categorySource = XDDFDataSourcesFactory.fromArray(categoryData);

            // 值数据:饼图中的圆形显示
            Double[] valueData = new Double[]{642.0, 147.6, 182.4, 99.6};
            XDDFNumericalDataSource<Double> valueSource = XDDFDataSourcesFactory.fromArray(valueData);

            // 创建饼图对象,饼状图不需要X,Y轴,只需要数据集即可
            XDDFPieChartData pieChart = (XDDFPieChartData) chart.createData(ChartTypes.PIE, null, null);

            // 加载饼图数据集
            XDDFPieChartData.Series pieSeries = (XDDFPieChartData.Series) pieChart.addSeries(categorySource, valueSource);
            pieSeries.setTitle("视力分布", null); // 系列提示标题

            // 绘制饼图
            chart.plot(pieChart);

            document.createParagraph(); // 空行

        } catch (Exception e) {
            System.err.println("创建饼图时出错: " + e.getMessage());
            addChartPlaceholder(document, "饼图 - 近视程度分布");
        }
    }

    private static void addDataTable(XWPFDocument document) {
        // 表格标题
        addChartTitle(document, "4. 详细数据统计");

        // 创建表格
        XWPFTable table = createTableSafely(document, 7, 7);

        // 设置表头
        String[] headers = {"年级", "筛查人数", "正常视力", "轻度近视", "中度近视", "高度近视", "近视率%"};
        setTableRowSafely(table, 0, headers, true, 10);

        // 填充数据
        String[][] tableData = {
                {"一年级", "200", "158", "25", "12", "5", "21.0%"},
                {"二年级", "200", "142", "32", "18", "8", "29.0%"},
                {"三年级", "200", "115", "45", "28", "12", "42.5%"},
                {"四年级", "200", "89", "58", "35", "18", "55.5%"},
                {"五年级", "200", "66", "67", "42", "25", "67.0%"},
                {"六年级", "200", "72", "75", "48", "32", "77.5%"}
        };

        for (int i = 0; i < tableData.length; i++) {
            setTableRowSafely(table, i + 1, tableData[i], false, 9);
        }

        document.createParagraph(); // 空行
    }

    private static void addConclusion(XWPFDocument document) {
        XWPFParagraph conclusionTitle = document.createParagraph();
        XWPFRun titleRun = conclusionTitle.createRun();
        titleRun.setText("分析与建议:");
        titleRun.setBold(true);
        titleRun.setFontSize(12);

        XWPFParagraph contentPara = document.createParagraph();
        XWPFRun contentRun = contentPara.createRun();
        contentRun.setText("• 近视率呈逐年上升趋势,高年级学生近视问题尤为突出\n"
                + "• 六年级近视率高达77.5%,需要重点关注和干预\n"
                + "• 高度近视比例随年级升高而增加,需加强早期干预\n"
                + "• 建议加强眼保健操质量监督,确保每天户外活动时间\n"
                + "• 定期开展视力保护健康教育,提高学生和家长重视程度\n"
                + "• 对已近视学生建立视力档案,定期跟踪视力变化情况");
        contentRun.setFontSize(11);
    }

    // 辅助方法:添加折线图系列
    private static void addLineSeries(XDDFLineChartData chart, XDDFCategoryDataSource categoryData,
                                      XDDFNumericalDataSource<Double> valueData, String title, MarkerStyle markerStyle) {
        XDDFLineChartData.Series series = (XDDFLineChartData.Series) chart.addSeries(categoryData, valueData);
        series.setTitle(title, null);
        series.setSmooth(false); // 折线
        series.setMarkerSize((short) 6); // 标记点大小
        series.setMarkerStyle(markerStyle); // 标记点样式
    }

    // 辅助方法:添加柱状图系列
    private static void addBarSeries(XDDFBarChartData chart, XDDFCategoryDataSource categoryData,
                                     XDDFNumericalDataSource<Double> valueData, String title) {
        XDDFBarChartData.Series series = (XDDFBarChartData.Series) chart.addSeries(categoryData, valueData);
        series.setTitle(title, null);
    }

    // 辅助方法:添加图表标题
    private static void addChartTitle(XWPFDocument document, String title) {
        XWPFParagraph titlePara = document.createParagraph();
        XWPFRun titleRun = titlePara.createRun();
        titleRun.setText(title);
        titleRun.setBold(true);
        titleRun.setFontSize(12);
        document.createParagraph();
    }

    // 辅助方法:图表占位符
    private static void addChartPlaceholder(XWPFDocument document, String chartTitle) {
        XWPFParagraph placeholder = document.createParagraph();
        placeholder.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun run = placeholder.createRun();
        run.setText("[" + chartTitle + " - 图表生成失败]");
        run.setColor("FF0000");
        run.setItalic(true);
        document.createParagraph();
    }

    // 安全创建表格
    private static XWPFTable createTableSafely(XWPFDocument document, int rows, int cols) {
        XWPFTable table = document.createTable();

        // 创建表头行
        XWPFTableRow headerRow = table.getRow(0);
        if (headerRow == null) {
            headerRow = table.createRow();
        }

        // 确保表头行有足够的单元格
        while (headerRow.getTableCells().size() < cols) {
            headerRow.addNewTableCell();
        }

        // 创建数据行
        for (int i = 1; i < rows; i++) {
            XWPFTableRow dataRow = table.createRow();
            while (dataRow.getTableCells().size() < cols) {
                dataRow.addNewTableCell();
            }
        }

        // 设置表格宽度
        CTTbl ctTbl = table.getCTTbl();
        CTTblPr tblPr = ctTbl.getTblPr() == null ? ctTbl.addNewTblPr() : ctTbl.getTblPr();
        CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
        tblWidth.setType(STTblWidth.DXA);
        tblWidth.setW(BigInteger.valueOf(9000));

        return table;
    }

    // 安全设置表格行
    private static void setTableRowSafely(XWPFTable table, int rowIndex, String[] data,
                                          boolean isHeader, int fontSize) {
        if (table == null || data == null) return;

        XWPFTableRow row;
        if (rowIndex < table.getNumberOfRows()) {
            row = table.getRow(rowIndex);
        } else {
            row = table.createRow();
        }

        // 确保行有足够的单元格
        while (row.getTableCells().size() < data.length) {
            row.addNewTableCell();
        }

        for (int i = 0; i < data.length && i < row.getTableCells().size(); i++) {
            XWPFTableCell cell = row.getCell(i);
            if (cell != null) {
                setCellContentSafely(cell, data[i], isHeader, fontSize);
            }
        }
    }

    // 安全设置单元格内容
    private static void setCellContentSafely(XWPFTableCell cell, String text,
                                             boolean isHeader, int fontSize) {
        try {
            // 清除现有内容
            for (int i = cell.getParagraphs().size() - 1; i >= 0; i--) {
                cell.removeParagraph(i);
            }

            // 添加新段落
            XWPFParagraph paragraph = cell.addParagraph();
            paragraph.setAlignment(ParagraphAlignment.CENTER);

            // 创建运行并设置文本
            XWPFRun run = paragraph.createRun();
            run.setText(text != null ? text : "");
            run.setFontSize(fontSize);

            if (isHeader) {
                run.setBold(true);
            }

        } catch (Exception e) {
            System.err.println("设置单元格内容失败: " + e.getMessage());
            try {
                cell.setText(text != null ? text : "");
            } catch (Exception ex) {
                System.err.println("备用设置方法也失败: " + ex.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        // 导出到指定目录
        String outputDir = "D:/视力报告目录";
        String fileName = "综合视力分析报告_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".docx";
        String outputPath = outputDir + "\\" + fileName;

        exportCombinedReport(outputPath);
    }
}
<dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</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.apache.poi</groupId>
        <artifactId>poi</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.xmlbeans</groupId>
        <artifactId>xmlbeans</artifactId>
        <version>3.1.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>ooxml-schemas</artifactId>
        <version>1.4</version>
    </dependency>
————————————————
版权声明:本文为CSDN博主「zhao_java_drao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhao_java_drao/article/details/124303418

  

  

https://www.bilibili.com/opus/608370314915365852

posted on 2025-11-10 16:55  IT-QI  阅读(0)  评论(0)    收藏  举报