POI生成動態模板PPT報告

Note

最新更新了github,有問題歡迎評論。感謝評論+討論的同在奮鬥的人

需求:(項目又一反人類需求)不同的樣式模板,替換數據生成新的PPT

花了我4天時間,轉載請註明作者感謝~

Github

地址:https://github.com/ithuhui/hui-core-autoreport
分支:master
位置:com.hui.core.report.ReportApp

Core

效果圖:

在這裏插入圖片描述

在這裏插入圖片描述

  • 讀取模板,樣式不變只替換其中數據
  • POI操作
  • 數據封裝

Code

  1. 接口設計

    流程: 讀取模板 -》 構建每一頁的PPT(圖表,表格,文本框) -》把傳入的數據參數進行替換 -》保存生成新的PPT

在這裏插入圖片描述

  1. 接口實現

    /**
     * <b><code>PowerPointGenerator</code></b>
     * <p/>
     * Description:
     * <p/>
     * <b>Creation Time:</b> 2018/10/23 21:15.
     *
     * @author Hu Weihui
     */
    public class PowerPointGenerator {
    
        private PowerPointGenerator() {
        }
    
        /**
         * PPT構造方法
         *
         * @param templateFilePath
         * @param destFilePath
         * @param slideDataMap
         * @throws IOException
         */
        public static void generatorPowerPoint(String templateFilePath, String destFilePath, Map<Integer, SlideData> slideDataMap) throws IOException, NoSuchChartTypeException {
            XMLSlideShow ppt = readPowerPoint(templateFilePath);
            List<XSLFSlide> slideList = ppt.getSlides();
            for (XSLFSlide slide : slideList) {
                int slidePage = slide.getSlideNumber();
                SlideData slideData = slideDataMap.get(slidePage);
                generatorSlide(slide, slideData);
            }
            savePowerPoint(ppt, destFilePath);
        }
    
        /**
         * 保存ppt到指定路徑
         *
         * @param ppt
         * @param outputFilePath
         * @throws IOException
         */
        private static void savePowerPoint(XMLSlideShow ppt, String outputFilePath) throws IOException {
            try (
                    FileOutputStream fileOutputStream = new FileOutputStream(outputFilePath);
    
            ) {
                ppt.write(fileOutputStream);
                ppt.close();
            }
        }
    
    
        /**
         * 讀取模板庫PPT
         *
         * @param inputFilePath
         * @return
         * @throws IOException
         */
        private static XMLSlideShow readPowerPoint(String inputFilePath) throws IOException {
            FileInputStream fileInputStream = new FileInputStream(inputFilePath);
            XMLSlideShow ppt = new XMLSlideShow(fileInputStream);
            return ppt;
        }
    
    
        /**
         * 替換每一頁數據
         *
         * @param slide
         * @param slideData
         * @throws IOException
         */
        private static void generatorSlide(XSLFSlide slide, SlideData slideData) throws IOException, NoSuchChartTypeException {
            List<POIXMLDocumentPart> partList = slide.getRelations();
            int chartNum = 0;
            for (POIXMLDocumentPart part : partList) {
                if (part instanceof XSLFChart) {
                    List<SeriesData> seriesDataList = slideData.getChartDataList().get(chartNum).getSeriesDataList();
                    generatorChart((XSLFChart) part, seriesDataList);
                    chartNum++;
                }
            }
    
    
            List<XSLFShape> shapeList = slide.getShapes();
    
            for (XSLFShape shape : shapeList) {
                Map<String, String> textMap = slideData.getTextMap();
                List<TableData> tableDataList = slideData.getTableDataList();
                int tableNum = 0;
                //判斷文本框
                if (shape instanceof XSLFTextShape) {
                    generatorTextBox((XSLFTextShape) shape, textMap);
                }
                //判斷表格
                if (shape instanceof XSLFTable) {
                    List<TableRowData> tableRowDataList = tableDataList.get(tableNum).getTableRowDataList();
                    generatorTable((XSLFTable) shape, tableRowDataList);
    
                }
            }
    
        }
    
    
        /**
         * 構造圖表
         *
         * @param chart
         * @param seriesDataList
         * @throws IOException
         */
        private static void generatorChart(XSLFChart chart, List<SeriesData> seriesDataList) throws IOException, NoSuchChartTypeException {
            if (seriesDataList.size() < 1) {
                return;
            }
            GraphUtils.refreshGraph(chart, seriesDataList);
        }
    
        /**
         * 構造文本
         *
         * @param textShape
         * @param textMap
         */
        private static void generatorTextBox(XSLFTextShape textShape, Map<String, String> textMap) {
            List<XSLFTextParagraph> textParagraphList = textShape.getTextParagraphs();
            for (XSLFTextParagraph textParagraph : textParagraphList) {
                String text = textParagraph.getText();
                String regex = "\\$\\{.*?\\}";
                Pattern pattern = Pattern.compile(regex);
                Matcher matcher = pattern.matcher(text);
                List<String> keys = new ArrayList<>();
                while (matcher.find()) {
                    keys.add(matcher.group());
                }
                for (String key : keys) {
                    String textKey = key.substring(2, key.length() - 1);
                    text = text.replace(key, textMap.get(textKey) == null ? " " : textMap.get(textKey));
                }
    
                List<XSLFTextRun> textRuns = textParagraph.getTextRuns();
                for (XSLFTextRun textRun : textRuns) {
                    String textStr = textRun.getRawText() == null ? "" : textRun.getRawText();
                    textRun.setText("");
                }
                if (textRuns.size() > 0) {
                    textRuns.get(0).setText(text);
                }
            }
        }
    
        /**
         * 構造表格
         *
         * @param table
         * @param tableDataList
         */
        private static void generatorTable(XSLFTable table, List<TableRowData> tableDataList) {
            List<XSLFTableRow> rows = table.getRows();
            int rowSize = rows.size() - 1;
            for (int i = 0; i < tableDataList.size(); i++) {
                if (i < rowSize) {
                    List<XSLFTableCell> cells = rows.get(i + 1).getCells();
                    for (int j = 0; j < tableDataList.get(i).getDataList().size(); j++) {
                        String s = tableDataList.get(i).getDataList().get(j);
                        cells.get(j).setText(s);
                    }
                } else {
                    table.addRow();
                    XSLFTableRow row = rows.get(i + 1);
                    for (int j = 0; j < tableDataList.get(i).getDataList().size(); j++) {
                        String s = tableDataList.get(i).getDataList().get(j);
                        row.addCell().setText(s);
                    }
                }
            }
            table.getCell(0, 0).setText("");
        }
    
  2. 圖表的構造工具

    圖表構造工具沒時間重構,只有個demo,這裏只挑core代碼進行記錄。

    • 判斷圖表類型

    參考別人,判斷圖表的類型真的是反人類,但是暫時還沒找到其他的方法。

            String chartType = "";
            CTPlotArea plotArea = part.getCTChart().getPlotArea();
            if (plotArea.getLineChartList().size() != 0) {
                chartType = "lie";
            }
            if (plotArea.getBarChartList().size() != 0) {
                chartType = "bar";
            }
            if (plotArea.getLineChartList().size() != 0
                    && plotArea.getBarChartList().size() != 0) {
                chartType = "barAndlie";
            }
            if (plotArea.getPieChartList().size() != 0) {
                chartType = "pie";
            }
    
            if (chartType == null) {
                throw new NoSuchChartTypeException("no Such Chart Type be found");
            } else {
                return chartType;
            }
    
    
    • 刷新圖表
        // 創建一個excel
                Workbook wb = new XSSFWorkbook();
                Sheet sheet = wb.createSheet();
    
                // 先生成excel表格,在刷新函數中往excel表格中添加數據
                for (int i = 0; i <= seriesDataList.get(0).getCategoryDataList()
                        .size(); i++) {
                    sheet.createRow(i);
                    for (int j = 0; j <= seriesDataList.size(); j++) {
                        sheet.getRow(i).createCell(j);
                    }
                }
                CTChart ctChart = chart.getCTChart();
                // 獲取圖表區域
                CTPlotArea plotArea = ctChart.getPlotArea();
    
                // 獲取柱狀圖表
                CTBarChart barChart = plotArea.getBarChartArray(0);
                
                // 獲取圖表的系列
                for (int i = 0; i < barChart.getSerList().size(); i++) {
                        CTSerTx barTx = barChart.getSerArray(i).getTx();
                        barTx.getStrRef().getStrCache().getPtArray(0)
                                .setV(seriesDataList.get(i).getSeriesName());
                        sheet.getRow(0).getCell(i + 1)
                                .setCellValue(seriesDataList.get(0).getSeriesName());
                        String barTitleRef = new CellReference(sheet.getSheetName(), 0,
                                i + 1, true, true).formatAsString();
                        barTx.getStrRef().setF(barTitleRef);
                        CTAxDataSource barCat = barChart.getSerArray(i).getCat();
                        CTNumDataSource barVal = barChart.getSerArray(i).getVal();
                        refreshGraphContent(sheet, barCat, barVal, seriesDataList.get(i),
                                i + 1);
                    }
    
                }
    
                // 更新嵌入的workbook
                POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
                OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();
                 wb.write(xlsOut);刷新圖表數據
    
    • 刷新圖表Excel內容
    /**
         * 刷新圖表內容.
         *
         * @param sheet 
         * @param cat 
         * @param val 
         * @param seriesData 
         * @param cellNum 
         * @author Hu Weihui
         */
        private static void refreshGraphContent(final Sheet sheet,
                                                final CTAxDataSource cat, final CTNumDataSource val,
                                                final SeriesData seriesData, final int cellNum) {
            // 獲取類別
            CTStrData strData = cat.getStrRef().getStrCache();
            // 獲取系列對應的值
            CTNumData numData = val.getNumRef().getNumCache();
            // strData.set
            strData.setPtArray((CTStrVal[]) null); // unset old axis text
            numData.setPtArray((CTNumVal[]) null); // unset old values
    
            // set model
            long idx = 0;
            int rownum = 1;
            for (CategoryData categoryData : seriesData.getCategoryDataList()) {
                CTNumVal numVal = numData.addNewPt();
                numVal.setIdx(idx);
                numVal.setV(categoryData.getVal() + "");
    
                CTStrVal sVal = strData.addNewPt();
                sVal.setIdx(idx);
                sVal.setV(categoryData.getCategoryName());
    
                idx++;
                rownum++;
            }
    
            // 設置excel的值
            sheet.getRow(0).getCell(cellNum).setCellValue(seriesData.getSeriesName());
            for (int i = 1; i < sheet.getLastRowNum() + 1; i++) {
                sheet.getRow(i).getCell(cellNum).setCellValue(
                        seriesData.getCategoryDataList().get(i - 1).getVal());
            }
            // 設置excel的標題
            for (int i = 0; i < seriesData.getCategoryDataList().size(); i++) {
                String serName = seriesData.getCategoryDataList().get(i).getCategoryName();
                sheet.getRow(i + 1).getCell(0).setCellValue(serName);
            }
    
            numData.getPtCount().setVal(idx);
            strData.getPtCount().setVal(idx);
    
            String numDataRange = new CellRangeAddress(1, rownum - 1, cellNum,
                    cellNum).formatAsString(sheet.getSheetName(), true);
            val.getNumRef().setF(numDataRange);
            String axisDataRange = new CellRangeAddress(1, rownum - 1, 0, 0)
                    .formatAsString(sheet.getSheetName(), true);
            cat.getStrRef().setF(axisDataRange);
    
        }
    
    1. 封裝能提供一頁PPT的數據實體
    /**
     * <b><code>SlideData</code></b>
     * <p/>
     * Description:一頁PPT的內容
     * <p/>
     * <b>Creation Time:</b> 2018/10/23 19:34.
     *
     * @author huweihui
     */
    public class SlideData implements Serializable {
    
        private static final long serialVersionUID = -69655131782023929L;
    
        private Integer slidePage;//頁碼
    
        private List<TableData> tableDataList;//表格數據(有可能有多個表格)
    
        private List<ChartData> chartDataList;//圖表數據(有可能有多個圖表)
    
        private Map<String,String> textMap;//文本框裏面文本數據
    }
    
    /**
     * <b><code>SeriesData</code></b>
     * <p/>
     * Description:圖表的系列(不能想象的話可以PPT新建一個柱狀圖就看到了)
     * <p/>
     * <b>Creation Time:</b> 2018/10/19 11:37.
     *
     * @author huweihui
     */
    public class SeriesData {
        // 系列名稱
        private String seriesName;
    
        // 所有類別的值
        private List<CategoryData> categoryDataList = new ArrayList<>();
    }
    
    /**
     * <b><code>SeriesData</code></b>
     * <p/>
     * Description:圖表的類別
     * <p/>
     * <b>Creation Time:</b> 2018/10/19 11:37.
     *
     * @author huweihui
     */
    public class CategoryData {
        // 類別名稱
        private String categoryName;
    
        // 類別值
        private double Val;
    }
    
    /**
     * <b><code>ChartData</code></b>
     * <p/>
     * Description:圖表數據
     * <p/>
     * <b>Creation Time:</b> 2018/10/24 11:46.
     *
     * @author huweihui
     */
    public class ChartData {
        private List<SeriesData> seriesDataList;
    }
    
    /**
     * <b><code>TableData</code></b>
     * <p/>
     * Description:表格一行的數據
     * <p/>
     * <b>Creation Time:</b> 2018/10/23 11:34.
     *
     * @author huweihui
     */
    public class TableRowData {
        private List<String> dataList = new ArrayList<>();
    
    }
    
    /**
     * <b><code>TableData</code></b>
     * <p/>
     * Description:表格數據
     * <p/>
     * <b>Creation Time:</b> 2018/10/24 14:19.
     *
     * @author huweihui
     */
    public class TableData {
        private List<TableRowData> tableRowDataList;
    }
    
    
    

總結

1.代碼並不全,但核心思想和核心代碼都出來了,以後整理放到github再更新鏈接。

2.這裏是PPT的模板構造工具,業務上傳過來的數據解析成SlideData這個類。就可以根據模板生成PPT

3.數據獲取的配置也是個考驗。我們是把數據和查找的SQL放到PPT模板讀取並執行SQL後替換數據到PPT

4.讀取Config 更爲複雜,但項目的業務不一樣。往後更新一個DEMO可跑單元測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章