目录
1、前言
2、了解pptx文件结构
3、POI组件
3.1、引入依赖
3.2、常见的类
3.3、实现原理
3.4、关键代码片段
3.4.1、获取ppt实例
3.4.2、获取每页幻灯片
3.4.3、循环遍历幻灯片处理
3.4.3.1、文本
3.4.3.2、饼图
3.4.3.3、柱状图
3.4.3.4、表格
3.4.3.5、本地文件连接
3.4.3.6、入口主类
1、前言
项目中有时候需要实现导出ppt格式报告,生成ppt文件的方式有很多,常见的有poi,aspose,pptx4j。
Apache POI,适合需要处理PPT基础功能的情况,免费开源。
Aspose.Slides,适合企业级应用,功能强大但收费。
Docx4j + pptx4j,较低层次的PPT操作工具,适合需要与docx4j一同使用的项目。
现在基本项目中都依赖了poi,因此这里首选poi来实现。基本的实现包括:文字占位替换,表格生成,报表生成(包括饼图,柱状图),超文本连接替换。
2、了解pptx文件结构
常见的pptx文件,实际上是基于XML的压缩文件。我们将.pptx文件的后缀改成.zip。即可直接解压缩出来内部的文件内容。通常包括以下几个主要部分:
- [Content_Types].xml:描述PPTX文件的内容类型,用于指定各个组件的格式(如幻灯片、文本、图像等)。
- docProps:包含文件属性,分为两部分:
-
core.xml:存储核心属性,如标题、作者、主题、创建日期等。
-
app.xml:存储应用属性,如幻灯片数量、主题、文档内容等。
-
- ppt文件夹 :PPTX的主要内容,包括以下子文件夹和文件:
- slides:包含每张幻灯片的内容(如文本、图像、动画等),每张幻灯片都对应一个XML文件。
- slides/_rels:每张幻灯片的关系文件,描述幻灯片内容中图像、视频、音频等的关联关系。
- media:存储幻灯片中包含的媒体文件(如图像、视频和音频文件)。
- theme:定义幻灯片的主题样式,包含配色方案、字体等。
- charts:存储PPT中的图表数据。
- tables:存储PPT中的表格信息。
- notesSlides:包含每张幻灯片的演讲者备注内容。
- embeddings:PPT报表关联的Excel文件。
- _rels文件夹:该文件夹用于管理文件之间的关系,通常包含一个**.rels**文件,描述各组件之间的关联性,比如幻灯片、媒体、样式等的链接关系。
由于我们这次需要渲染多种报表,报表的生成本质是依赖于Excel文本的数据填充,以及公式的计算和渲染。因此我们将会重点关注ppt\charts图表数据和ppt\embeddings的Excel文件。
3、POI组件
3.1、引入依赖
<dependencies><!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.3.0</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.28</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version></dependency>
</dependencies>
3.2、常见的类
术语 | 解释 |
XMLSlideShow | PPT演示文稿,通常指一份.pptx文件。代码中会优先获取该ppt实例,根据该实例获取ppt内具体元素。 |
XSLFSlide | 幻灯片,指ppt内的每一页。通过该对象可以获取每一页幻灯片内的所有元素。 |
XSLFShape | 幻灯片内的所有形状,比如图形元素等。 |
XSLFTextShape | XSLFShape的子类,指文本框元素 |
XSLFTable | XSLFShape的子类,指表格元素 |
XSLFGraphicFrame | XSLFShape的子类,指表格元素 |
XSLFChart | 报表元素 |
CTPieChart | 饼图元素 |
CTBarChart | 柱状图元素 |
3.3、实现原理
简单介绍下POI渲染PPT的原理:
- 读取pptx模板,new XMLSlideShow(inputStream)得到ppt实例;
- 通过getSlides()方法获取该ppt的所有幻灯片集合;
- 循环遍历所有的slides,通过getShapes()获取XSLFShape,每个幻灯片上的形状;
- 结合形状的类型,或报表的标题,以及该幻灯片的页码,可以确定我们需要渲染的某一个报表图形;
- 将shape转成对应图形元素,如果是文字类型,直接设置对应文本内容即可;
- 如果是报表类型,根据对应的报表类型转换后,渲染对应的Ser,Cat,Val等属性;本质其实是ppt关联了一份内置的excel,刷新excel索引渲染出报表;如:
- 具体的cat和val的属性节点,每份ppt解压出来后,每个报表都会对应一份chartxx.xml,打开这份xml即为这个报表对应的节点信息。如:
- 最后渲染报表索引。
3.4、关键代码片段
3.4.1、获取ppt实例
public class PowerPointUtil {public static XMLSlideShow getXmlSlideShow(FileInputStream inputStream) {try {return new XMLSlideShow(inputStream);} catch (IOException e) {System.err.println("初始化ppt实例错误");}return null;}}
3.4.2、获取每页幻灯片
FileInputStream inputStream = new FileInputStream("模板.0.pptx");
XMLSlideShow ppt = PowerPointUtil.getXmlSlideShow(inputStream);// 获取幻灯片 XSLFSlide
List<XSLFSlide> slides = ppt.getSlides();
3.4.3、循环遍历幻灯片处理
3.4.3.1、文本
如果是文本,直接将ppt需要渲染的文字替换为关键字符,如PA_DEVICE、PA_SUPPLIER等。
if (shape instanceof XSLFTextShape) {XSLFTextShapeImpl.generalXSLFText(ppt, DataParam.getTextDataMap());
}public class XSLFTextShapeImpl {public static void generalXSLFText(XMLSlideShow ppt, Map<String, String> dataParam){// 获取幻灯片 XSLFSlideList<XSLFSlide> slides = ppt.getSlides();if(CollUtil.isEmpty(slides)){return ;}slides.forEach(slide -> {List<XSLFShape> shapes = slide.getShapes();if(CollUtil.isEmpty(shapes)){return ;}shapes.stream().filter(shape -> shape instanceof XSLFTextShape).forEach(shape -> {XSLFTextShape textShape = (XSLFTextShape) shape;for (XSLFTextParagraph textParagraph : textShape.getTextParagraphs()) {for (XSLFTextRun textRun : textParagraph.getTextRuns()) {final String[] text = {textRun.getRawText()};dataParam.forEach((key, value) -> text[0] = text[0].replace(key, value));textRun.setText(text[0]);}}});});}}
其中dataParam数据为:
/*** 文字占位* @return*/
public static Map<String, String> getTextDataMap(){// 文本数据映射表Map<String, String> textDataMap = new HashMap<>();textDataMap.put("PA_TITLE", "测试报告");String formatted = DateUtil.format(DateUtil.date(), "yyyy年MM月dd日");textDataMap.put("PA_CREATE_TIME", formatted);textDataMap.put("PA_SUPPLIER_C", "10");textDataMap.put("PA_DEVICE_C", "50");textDataMap.put("PA_DEVICE_PER", "80%");return textDataMap;
}
3.4.3.2、饼图
/**** @param chart* @param is3DPie* @param dataParam* @throws IOException* @throws InvalidFormatException*/
public static void generalXSLFPieChart(XSLFChart chart, boolean is3DPie, List<DataParam.NamedValue> dataParam) throws IOException, InvalidFormatException {if(CollUtil.isEmpty(dataParam)){return ;}XSSFWorkbook workbook = chart.getWorkbook();XSSFSheet sheetAt = workbook.getSheetAt(0);for (int i = 0; i < dataParam.size(); i++) {int j = i + 1;sheetAt.createRow(j);sheetAt.getRow(j).createCell(0).setCellValue(dataParam.get(i).getName());sheetAt.getRow(j).createCell(1).setCellValue(dataParam.get(i).getValue());}workbook.write(chart.getPackagePart().getOutputStream());// 刷新图表缓存CTPlotArea plotArea = chart.getCTChart().getPlotArea();// 是3D饼图还是扁平饼图List<CTPieSer> serList = is3DPie ? plotArea.getPie3DChartArray(0).getSerList() : plotArea.getPieChartArray(0).getSerList();for (CTPieSer ser : serList) {// 更新excel范围rangeCTNumDataSource numDataSource = ser.getVal();CTAxDataSource catDataSource = ser.getCat();// TODO cat 也可能是 numReflong ptCatCnt = catDataSource.getStrRef().getStrCache().getPtCount().getVal();long ptNumCnt = numDataSource.getNumRef().getNumCache().getPtCount().getVal();for (int i = 0; i < dataParam.size(); i++) {DataParam.NamedValue cellValue = dataParam.get(i);CTStrVal cat = ptCatCnt > i ? catDataSource.getStrRef().getStrCache().getPtArray(i): catDataSource.getStrRef().getStrCache().addNewPt();cat.setIdx(i);cat.setV(cellValue.getName());CTNumVal val = ptNumCnt > i ? numDataSource.getNumRef().getNumCache().getPtArray(i): numDataSource.getNumRef().getNumCache().addNewPt();val.setIdx(i);val.setV(String.format("%.2f", Double.parseDouble(cellValue.getValue())));}catDataSource.getStrRef().setF(replaceRowEnd(catDataSource.getStrRef().getF(),ptCatCnt,dataParam.size()));numDataSource.getNumRef().setF(replaceRowEnd(numDataSource.getNumRef().getF(),ptNumCnt,dataParam.size()));// 更新个数catDataSource.getStrRef().getStrCache().getPtCount().setVal(dataParam.size());numDataSource.getNumRef().getNumCache().getPtCount().setVal(dataParam.size());}
}
3.4.3.3、柱状图
public class XSLFBarChartShapeImpl extends AbstractXSLFChartShape {private static final String[] COL_TITLE_F = {"B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};/*** 2列数据柱状图* @param chart* @param dataParam* @throws IOException* @throws InvalidFormatException*/public static void generalXSLFBarChart(XSLFChart chart, List<DataParam.NamedValue> dataParam, boolean isSort) throws IOException, InvalidFormatException {XSSFWorkbook workbook = chart.getWorkbook();XSSFSheet sheetAt = workbook.getSheetAt(0);if(isSort){dataParam = dataParam.stream().sorted(Comparator.comparing(DataParam.NamedValue::getValue)).collect(Collectors.toCollection(LinkedList::new));}for (int i = 0; i < dataParam.size(); i++) {int j = i + 1;sheetAt.createRow(j);sheetAt.getRow(j).createCell(0).setCellValue(dataParam.get(i).getName());sheetAt.getRow(j).createCell(1).setCellValue(dataParam.get(i).getValue());}workbook.write(chart.getPackagePart().getOutputStream());// 刷新图表缓存CTPlotArea plotArea = chart.getCTChart().getPlotArea();CTBarChart ctChart = plotArea.getBarChartArray(0);for (CTBarSer ser : ctChart.getSerList()) {// 更新excel范围rangeCTNumDataSource numDataSource = ser.getVal();CTAxDataSource catDataSource = ser.getCat();// TODO cat 也可能是 numReflong ptCatCnt = catDataSource.getStrRef().getStrCache().getPtCount().getVal();long ptNumCnt = numDataSource.getNumRef().getNumCache().getPtCount().getVal();for (int i = 0; i < dataParam.size(); i++) {DataParam.NamedValue cellValue = dataParam.get(i);CTStrVal cat = ptCatCnt > i ? catDataSource.getStrRef().getStrCache().getPtArray(i): catDataSource.getStrRef().getStrCache().addNewPt();cat.setIdx(i);cat.setV(cellValue.getName());CTNumVal val = ptNumCnt > i ? numDataSource.getNumRef().getNumCache().getPtArray(i): numDataSource.getNumRef().getNumCache().addNewPt();val.setIdx(i);val.setV(String.valueOf(cellValue.getValue()));}catDataSource.getStrRef().setF(replaceRowEnd(catDataSource.getStrRef().getF(),ptCatCnt,dataParam.size()));numDataSource.getNumRef().setF(replaceRowEnd(numDataSource.getNumRef().getF(),ptNumCnt,dataParam.size()));// 更新个数catDataSource.getStrRef().getStrCache().getPtCount().setVal(dataParam.size());numDataSource.getNumRef().getNumCache().getPtCount().setVal(dataParam.size());}}/*** 多数据维度柱状图* @param chart* @param dataParamList* @param isSort* @throws IOException* @throws InvalidFormatException*/public static void generalXSLFBarChart2(XSLFChart chart, List<DataParam.CategoryNamedValue> dataParamList, boolean isSort) throws IOException, InvalidFormatException {if(CollUtil.isEmpty(dataParamList)){return ;}// 组装成Map<colCellKey, Map<rowTitleKey, value>>LinkedHashMap<String, LinkedHashMap<String, String>> dataParam = new LinkedHashMap<>();if(isSort){dataParamList = dataParamList.stream().sorted(Comparator.comparing(DataParam.CategoryNamedValue::getValue)).collect(Collectors.toList());}// 获取category名称集合List<String> rowTitleList = dataParamList.stream().map(DataParam.CategoryNamedValue::getCategory).distinct().collect(Collectors.toList());// 获取设备型号List<String> colTitleList = dataParamList.stream().map(DataParam.CategoryNamedValue::getName).distinct().collect(Collectors.toList());if(CollUtil.isEmpty(rowTitleList)){return ;}XSSFWorkbook workbook = chart.getWorkbook();XSSFSheet sheetAt = workbook.getSheetAt(0);AtomicInteger cellIndex = new AtomicInteger(1);XSSFRow row0 = sheetAt.createRow(0);row0.createCell(0).setCellValue("");rowTitleList.forEach(rowTitle -> row0.createCell(cellIndex.getAndIncrement()).setCellValue(rowTitle));int i = 0;for (DataParam.CategoryNamedValue categoryNamedValue : dataParamList) {// entry是一行的数据int j = ++i;XSSFRow currentRow = sheetAt.createRow(j);currentRow.createCell(0).setCellValue(categoryNamedValue.getName());// 找到这个category所在的cellcurrentRow.createCell(rowTitleList.indexOf(categoryNamedValue.getCategory()) + 1).setCellValue(categoryNamedValue.getValue());}workbook.write(chart.getPackagePart().getOutputStream());// 刷新图表缓存CTPlotArea plotArea = chart.getCTChart().getPlotArea();CTBarChart ctChart = plotArea.getBarChartArray(0);for (int j = 0; j < ctChart.getSerList().size(); j++) {ctChart.removeSer(j);}for (int j = 0; j < rowTitleList.size(); j++) {CTBarSer ser = ctChart.addNewSer();// 设置系列ID (索引),为新系列分配一个唯一的 idCTUnsignedInt idx = ser.addNewIdx();idx.setVal(ctChart.sizeOfSerArray()); // 使用系列数量作为索引CTStrRef ctStrRef = ser.addNewTx().addNewStrRef();ctStrRef.setF("Sheet1!$" + COL_TITLE_F[j] + "$1");CTStrVal ctStrVal = ctStrRef.addNewStrCache().addNewPt();ctStrVal.setIdx(j);ctStrVal.setV(rowTitleList.get(j));}//for (int j = 0; j < ctChart.getSerList().size(); j++) {CTBarSer ctBarSer = ctChart.getSerList().get(j);// catCTAxDataSource catDataSource = ctBarSer.addNewCat();CTStrRef catRef = catDataSource.addNewStrRef();catRef.setF("Sheet1!$A$2:$A$" + (dataParamList.size() + 1));CTStrData catStrCache = catRef.addNewStrCache();CTNumDataSource valDataSource = ctBarSer.addNewVal();CTNumRef numRef = valDataSource.addNewNumRef();CTNumData numCache = numRef.addNewNumCache();numRef.setF("Sheet1!$" + COL_TITLE_F[j] + "$2:$" + COL_TITLE_F[j] + "$" + (colTitleList.size() + 1));for (int k = 0; k < colTitleList.size(); k++) {CTStrVal catStrVal = catStrCache.addNewPt();catStrVal.setIdx(k);catStrVal.setV(colTitleList.get(k));}// valfor (int k = 0; k < dataParamList.size(); k++) {DataParam.CategoryNamedValue categoryNamedValue = dataParamList.get(k);// 同一个系列,同一个cat下if (categoryNamedValue.getCategory().equalsIgnoreCase(ctBarSer.getTx().getStrRef().getStrCache().getPtArray(0).getV())) {int ptIdx = colTitleList.indexOf(categoryNamedValue.getName());if (ptIdx != -1) {CTNumVal numVal = numCache.addNewPt();numVal.setIdx(ptIdx); // 只有一个点,表示数量numVal.setV(categoryNamedValue.getValue());}}}}}/*** 竖向柱状图,这里是固定列只有数量* @param chart* @param dataParam* @throws IOException* @throws InvalidFormatException*/public static void generalXSLFVerticalBarChart(XSLFChart chart, List<DataParam.CategoryNamedValue> dataParamList) throws IOException, InvalidFormatException {if(CollUtil.isEmpty(dataParamList)){return ;}// 获取category名称集合List<String> rowTitleList = dataParamList.stream().map(DataParam.CategoryNamedValue::getCategory).distinct().collect(Collectors.toList());// 获取设备型号List<String> colTitleList = dataParamList.stream().map(DataParam.CategoryNamedValue::getName).distinct().collect(Collectors.toList());if(CollUtil.isEmpty(rowTitleList)){return ;}XSSFWorkbook workbook = chart.getWorkbook();XSSFSheet sheetAt = workbook.getSheetAt(0);AtomicInteger cellIndex = new AtomicInteger(1);XSSFRow row0 = sheetAt.createRow(0);row0.createCell(0).setCellValue("");rowTitleList.forEach(rowTitle -> row0.createCell(cellIndex.getAndIncrement()).setCellValue(rowTitle));int i = 0;for (String colCellValue : colTitleList) {// entry是一行的数据int j = ++i;XSSFRow currentRow = sheetAt.createRow(j);currentRow.createCell(0).setCellValue(colCellValue);dataParamList.stream().filter(data -> data.getName().equalsIgnoreCase(colCellValue)).forEach(data -> {// 找到这个category所在的cellint catCellIndex = rowTitleList.indexOf(data.getCategory());currentRow.createCell(catCellIndex + 1).setCellValue(data.getValue());});}workbook.write(chart.getPackagePart().getOutputStream());// 刷新图表缓存CTPlotArea plotArea = chart.getCTChart().getPlotArea();CTBarChart ctChart = plotArea.getBarChartArray(0);for (int j = 0; j < ctChart.getSerList().size(); j++) {ctChart.removeSer(j);ctChart.removeSer(j);}for (int j = 0; j < rowTitleList.size(); j++) {CTBarSer ser = ctChart.addNewSer();// 设置系列ID (索引),为新系列分配一个唯一的 idCTUnsignedInt idx = ser.addNewIdx();idx.setVal(ctChart.sizeOfSerArray()); // 使用系列数量作为索引CTStrRef ctStrRef = ser.addNewTx().addNewStrRef();ctStrRef.setF("Sheet1!$" + COL_TITLE_F[j] + "$1");CTStrVal ctStrVal = ctStrRef.addNewStrCache().addNewPt();ctStrVal.setIdx(j);ctStrVal.setV(rowTitleList.get(j));}//for (int j = 0; j < ctChart.getSerList().size(); j++) {CTBarSer ctBarSer = ctChart.getSerList().get(j);// catCTAxDataSource catDataSource = ctBarSer.addNewCat();CTStrRef catRef = catDataSource.addNewStrRef();catRef.setF("Sheet1!$A$2:$A$" + (dataParamList.size() + 1));CTStrData catStrCache = catRef.addNewStrCache();CTNumDataSource valDataSource = ctBarSer.addNewVal();CTNumRef numRef = valDataSource.addNewNumRef();CTNumData numCache = numRef.addNewNumCache();numRef.setF("Sheet1!$" + COL_TITLE_F[j] + "$2:$" + COL_TITLE_F[j] + "$" + (colTitleList.size() + 1));for (int k = 0; k < colTitleList.size(); k++) {CTStrVal catStrVal = catStrCache.addNewPt();catStrVal.setIdx(k);catStrVal.setV(colTitleList.get(k));}// valfor (int k = 0; k < dataParamList.size(); k++) {DataParam.CategoryNamedValue categoryNamedValue = dataParamList.get(k);// 同一个系列,同一个cat下if (categoryNamedValue.getCategory().equalsIgnoreCase(ctBarSer.getTx().getStrRef().getStrCache().getPtArray(0).getV())) {int ptIdx = colTitleList.indexOf(categoryNamedValue.getName());if (ptIdx != -1) {CTNumVal numVal = numCache.addNewPt();numVal.setIdx(ptIdx); // 只有一个点,表示数量numVal.setV(categoryNamedValue.getValue());}}}}}private static void sortValueAsc(List<Map<String, String>> listOfMaps){// 根据值排序listOfMaps.sort((mapA, mapB) -> {// 获取mapA中的第一个值String valueA = mapA.entrySet().iterator().next().getValue();// 获取mapB中的第一个值String valueB = mapB.entrySet().iterator().next().getValue();// 比较值进行排序return valueA.compareTo(valueB);});}
}
3.4.3.4、表格
public class XSLFTableShapeImpl {/**** @param shape* @param dataMapList*/public static void generalXSLFTable(XSLFTable shape, LinkedList<String> titleList, List<Map<String, String>> dataMapList, boolean isMerge, int mergeCol){if(CollUtil.isEmpty(dataMapList) || CollUtil.isEmpty(titleList)){return ;}if(isMerge && mergeCol >= titleList.size()){throw new IllegalArgumentException("表格头字段列数小于合并列数,请检查");}// 按同一列分组Map<String, List<Map<String, String>>> mergeListMap = new HashMap<>();mergeListMap.put("", dataMapList);if(isMerge){mergeListMap = dataMapList.stream().collect(Collectors.groupingBy(map -> map.get(titleList.get(mergeCol))));}// 填充数据到Excel表中,并处理单元格合并int rowNum = 1;for (Map.Entry<String, List<Map<String, String>>> entry : mergeListMap.entrySet()) {List<Map<String, String>> values = entry.getValue();int startRow = rowNum;// 填充每个供应商的型号和数量for (Map<String, String> dataMap : values) {rowNum++;XSLFTableRow row = shape.addRow();for (String title : titleList) {XSLFTableCell cell = row.addCell();setTableCellStyle(dataMap.get(title), cell);}// 合并供应商列的单元格if (isMerge && values.size() > 1) {shape.mergeCells(startRow, rowNum - 1, mergeCol, mergeCol);}}}}/**** @param text* @param cell*/private static void setTableCellStyle(String text, XSLFTableCell cell){XSLFTextParagraph p = cell.addNewTextParagraph();p.setTextAlign(TextParagraph.TextAlign.CENTER);XSLFTextRun r = p.addNewTextRun();r.setText(text);r.setFontSize(12.0); //// 设置单元格边框cell.setBorderColor(TableCell.BorderEdge.bottom, Color.LIGHT_GRAY);cell.setBorderColor(TableCell.BorderEdge.top, Color.LIGHT_GRAY);cell.setBorderColor(TableCell.BorderEdge.left, Color.LIGHT_GRAY);cell.setBorderColor(TableCell.BorderEdge.right, Color.LIGHT_GRAY);cell.setBorderWidth(TableCell.BorderEdge.bottom, 1.0);cell.setBorderWidth(TableCell.BorderEdge.top, 1.0);cell.setBorderWidth(TableCell.BorderEdge.left, 1.0);cell.setBorderWidth(TableCell.BorderEdge.right, 1.0);// 设置背景颜色cell.setFillColor(Color.WHITE);}}
3.4.3.5、本地文件连接
public class XSLFHyperlinkShapeImpl {public static void generalHyperLink(XSLFTextShape shape, String text, Path localFilePath) throws URISyntaxException {// 清除旧的文本内容shape.clearText();// 创建新的超链接文本XSLFTextRun textRun = shape.addNewTextParagraph().addNewTextRun();textRun.setText(text);textRun.setFontSize(12.0);// 创建文件链接URI fileUri = new URI("file:///" + localFilePath.toString().replace("\\", "/")); // 确保路径格式正确XSLFHyperlink hyperlink = textRun.createHyperlink();hyperlink.setAddress(fileUri.toString());}}
3.4.3.6、入口主类
public class PowerPointMainDemo {/*** 为了保持ppt模板报表以及其他图形的样式,这里采用的是直接替换原有excel关联数据,而不是重新生成。* 因此需要保证每个报表关联的excel至少有一条数据,来保证所获取的CTSer是有值的。* 表格除外,表格采用的是直接追加的形式,所以表格的模板上个除了标题,不能有其他行数据。* 这里表格暂时只支持单列的合并* @param args* @throws IOException* @throws InvalidFormatException* @throws URISyntaxException*/public static void main(String[] args) throws IOException, InvalidFormatException, URISyntaxException {// 读取PPT模板FileInputStream inputStream = new FileInputStream("模板.0.pptx");XMLSlideShow ppt = PowerPointUtil.getXmlSlideShow(inputStream);// 获取幻灯片 XSLFSlideList<XSLFSlide> slides = ppt.getSlides();for(XSLFSlide slide : slides){for (XSLFShape shape : slide.getShapes()) {// 处理文本。默认这里每一页的key都不一样,所以不需要根据页码来判定if (shape instanceof XSLFTextShape) {XSLFTextShapeImpl.generalXSLFText(ppt, DataParam.getTextDataMap());}// 处理报表if(shape instanceof XSLFGraphicFrame) {XSLFGraphicFrame graphicFrame = (XSLFGraphicFrame) shape;if (graphicFrame.hasChart()) {XSLFChart chart = graphicFrame.getChart();String text = chart.getTitleShape().getText();// 处理第6页的饼图if(slide.getSlideNumber() == 6 && text.equalsIgnoreCase("各厂商设备型号占比")){XSLFPieChartShapeImpl.generalXSLFPieChart(chart, false, DataParam.getSupplierPercentList());}// 处理第6页的竖向柱状图if(slide.getSlideNumber() == 6 && text.equalsIgnoreCase("各厂商设备型号分布")){XSLFBarChartShapeImpl.generalXSLFBarChart(chart, DataParam.getSupplierPercentList(), true);}// 处理第8页的横向柱状图if(slide.getSlideNumber() == 8 && text.equalsIgnoreCase("设备型号分布Top10")){XSLFBarChartShapeImpl.generalXSLFBarChart2(chart, DataParam.getSupplierModelCountList3(), true);}// 处理第8页的横向柱状图if(slide.getSlideNumber() == 14 && text.equalsIgnoreCase("设备持续运行时间(Top50)")){XSLFBarChartShapeImpl.generalXSLFBarChart2(chart, DataParam.getSupplierModelCountList3(), true);}// 处理第9页的3D饼图if(slide.getSlideNumber() == 9 && text.equalsIgnoreCase("全网设备生命周期分布")){XSLFPieChartShapeImpl.generalXSLFPieChart(chart, true, DataParam.getDeviceMaintainPercent());}// 处理第9页的3D饼图if(slide.getSlideNumber() == 10 && text.equalsIgnoreCase("各厂商设备维保信息统计")){XSLFBarChartShapeImpl.generalXSLFVerticalBarChart(chart, DataParam.getDeviceLifecycleList());}}}// 处理表格if(shape instanceof XSLFTable && slide.getSlideNumber() == 8) {XSLFTable table = (XSLFTable) shape;LinkedList<String> titleList = new LinkedList<>();titleList.add("设备厂商");titleList.add("设备型号");titleList.add("数量");XSLFTableShapeImpl.generalXSLFTable(table, titleList, DataParam.getSupplierModelCountListMap(), true, 0);}// 处理表格if(shape instanceof XSLFTable && slide.getSlideNumber() == 9) {XSLFTable table = (XSLFTable) shape;LinkedList<String> titleList = new LinkedList<>();titleList.add("厂商");titleList.add("设备型号");titleList.add("数量");titleList.add("EOM时间");titleList.add("EOS时间");XSLFTableShapeImpl.generalXSLFTable(table, titleList, DataParam.getSupplierModelCountList2(), false, 0);}// 处理文件连接if(slide.getSlideNumber() == 6) {if(shape instanceof XSLFTextShape){XSLFTextShape textShape = (XSLFTextShape) shape;List<XSLFTextParagraph> paragraphs = textShape.getTextParagraphs();for (XSLFTextParagraph paragraph : paragraphs) {if(StrUtil.isNotBlank(paragraph.getText()) && paragraph.getText().equalsIgnoreCase("设备数据表总览.xlsx")){XSLFHyperlinkShapeImpl.generalHyperLink(textShape, "设备数据表总览.xlsx", Paths.get(System.getProperty("user.dir"), "设备数据.xlsx"));}}}}}}// 输出新的PPT文件FileOutputStream outputStream = new FileOutputStream("报告模板v1-" + DateUtil.format(DateUtil.date(), "yyyyMMdd") + ".pptx");ppt.write(outputStream);outputStream.close();ppt.close();}
}