版权声明:本文为博主原创文章,未经博主允许不得转载。转载请附上博主博文网址,并标注作者。违者必究 https://blog.csdn.net/HuHui_/article/details/83350049
文章目录
前言
需求:(项目又一反人类需求)不同的样式模板,替换数据生成新的PPT
花了我4天时间,转载请注明作者感谢~
核心
效果图:
- 读取模板,样式不变只替换其中数据
- POI操作
- 数据封装
Core - Code
-
接口设计
流程: 读取模板 -》 构建每一页的PPT(图表,表格,文本框) -》把传入的数据参数进行替换 -》保存生成新的PPT
-
接口实现
/** * <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(""); }
-
图表的构造工具
图表构造工具没时间重构,只有个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); }
- 封装能提供一页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可跑单元测试