POI实现根据PPTX模板渲染PPT

目录

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。即可直接解压缩出来内部的文件内容。通常包括以下几个主要部分:

  1. [Content_Types].xml:描述PPTX文件的内容类型,用于指定各个组件的格式(如幻灯片、文本、图像等)。
  2. docProps:包含文件属性,分为两部分:
    • core.xml:存储核心属性,如标题、作者、主题、创建日期等。

    • app.xml:存储应用属性,如幻灯片数量、主题、文档内容等。

  3. ppt文件夹 :PPTX的主要内容,包括以下子文件夹和文件:
    • slides:包含每张幻灯片的内容(如文本、图像、动画等),每张幻灯片都对应一个XML文件。
    • slides/_rels:每张幻灯片的关系文件,描述幻灯片内容中图像、视频、音频等的关联关系。
    • media:存储幻灯片中包含的媒体文件(如图像、视频和音频文件)。
    • theme:定义幻灯片的主题样式,包含配色方案、字体等。
    • charts:存储PPT中的图表数据。
    • tables:存储PPT中的表格信息。
    • notesSlides:包含每张幻灯片的演讲者备注内容。
    • embeddings:PPT报表关联的Excel文件。
  4. _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的原理:

  1. 读取pptx模板,new XMLSlideShow(inputStream)得到ppt实例;
  2. 通过getSlides()方法获取该ppt的所有幻灯片集合;
  3. 循环遍历所有的slides,通过getShapes()获取XSLFShape,每个幻灯片上的形状;
  4. 结合形状的类型,或报表的标题,以及该幻灯片的页码,可以确定我们需要渲染的某一个报表图形;
  5. 将shape转成对应图形元素,如果是文字类型,直接设置对应文本内容即可;
  6. 如果是报表类型,根据对应的报表类型转换后,渲染对应的Ser,Cat,Val等属性;本质其实是ppt关联了一份内置的excel,刷新excel索引渲染出报表;如:

  1. 具体的cat和val的属性节点,每份ppt解压出来后,每个报表都会对应一份chartxx.xml,打开这份xml即为这个报表对应的节点信息。如:

  1. 最后渲染报表索引。

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();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/60910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sqli-labs靶场17-20关(每日四关)持续更新!!!

Less-17 打开靶场&#xff0c;发现页面比之前多了一行字 翻译过来就是&#xff0c;密码重置&#xff0c;大家肯定会想到&#xff0c;自己平时在日常生活中怎么密码重置&#xff0c;肯定是输入自己的用户名&#xff0c;输入旧密码&#xff0c;输入新密码就可以了&#xff0c;但…

node.js下载安装步骤整理

>> 进入node.js下载页面下载 | Node.js 中文网 >>点击 全部安装包 >>删除网址node后面部分&#xff0c;只保留如图所示部分&#xff0c;回车 >>点击进入v11.0.0/版本 >>点击下载node-v11.0.0-win-x64.zip(电脑是windows 64位操作系统适用) >…

ThinkServer SR658H V2服务器BMC做raid与装系统

目录 前提准备 一. 给磁盘做raid 二. 安装系统 前提准备 磁盘和系统BMC地址都已经准备好&#xff0c;可正常使用。 例&#xff1a; 设备BMC地址&#xff1a;10.99.240.196 一. 给磁盘做raid 要求&#xff1a; 1. 将两个894G的磁盘做成raid1 2. 将两块14902G的磁盘各自做…

SpringBoot配置类

在Spring Boot中&#xff0c;配置类是一种特殊的类&#xff0c;用于定义和配置Spring应用程序的各种组件、服务和属性。这些配置类通常使用Java注解来声明&#xff0c;并且可以通过Spring的依赖注入机制来管理和使用。 Spring 容器初始化时会加载被Component、Service、Reposi…

SpringBoot教程(二十五) | SpringBoot配置多个数据源

SpringBoot教程&#xff08;二十五&#xff09; | SpringBoot配置多个数据源 前言方式一&#xff1a;使用dynamic-datasource-spring-boot-starter引入maven依赖配置数据源动态切换数据源实战 方式二&#xff1a;使用AbstractRoutingDataSource1. 创建数据源枚举类2. 创建数据源…

ZooKeeper单机、集群模式搭建教程

单点配置 ZooKeeper在启动的时候&#xff0c;默认会读取/conf/zoo.cfg配置文件&#xff0c;该文件缺失会报错。因此&#xff0c;我们需要在将容器/conf/挂载出来&#xff0c;在制定的目录下&#xff0c;添加zoo.cfg文件。 zoo.cfg logback.xml 配置文件的信息可以从二进制包…

【大数据学习 | HBASE高级】hbase-phoenix 与二次索引应用

1. hbase-phoenix的应用 1.1 概述&#xff1a; 上面我们学会了hbase的操作和原理&#xff0c;以及外部集成的mr的计算方式&#xff0c;但是我们在使用hbase的时候&#xff0c;有的时候我们要直接操作hbase做部分数据的查询和插入&#xff0c;这种原生的方式操作在工作过程中还…

拆解测试显示Mac Mini (2024)固态硬盘并未锁定 互换硬盘后仍可使用

此前已经有维修达人尝试将 Mac Mini (2024) 固态硬盘上的 NAND 闪存拆下并替换实现扩容&#xff0c;例如可以从 256GB 扩容到 2TB。虽然接口类似于 NVMe M.2 SSD 但直接安装普通硬盘是无效的&#xff0c;苹果仍然通过某种机制检测硬盘是否能够兼容。 不过知名拆解网站 iFixit 的…

主界面获取个人信息客户端方

主界面获取个人信息客户端方 前言 上一集我们完成了websocket身份验证的内容&#xff0c;那么这一集开始我们将要配合MockServer来完成主界面获取个人信息的内容。 需求分析 我们这边是完成客户端那方的内容&#xff0c;当客户端登录成功之后&#xff0c;我们就要从服务器获…

Spring整合Redis

前言 在Spring项目中整合Redis&#xff0c;能显著提升数据缓存、分布式锁、会话管理等操作的效率。Jedis作为轻量级的Java Redis客户端&#xff0c;搭配Spring Data Redis模块&#xff0c;能够简化Redis的连接和数据操作&#xff0c;实现更高性能的读写与灵活的缓存管理。本文…

爬虫——Requests库的使用

在爬虫开发中&#xff0c;HTTP请求是与服务器进行交互的关键操作。通过发送HTTP请求&#xff0c;爬虫可以获取目标网页或接口的数据&#xff0c;而有效地处理请求和响应是爬虫能够高效且稳定运行的基础。Requests库作为Python中最常用的HTTP请求库&#xff0c;因其简洁、易用和…

LinkedHashMap实现LRU

LRU 环境&#xff1a;JDK11 最近接触LRU(Least Recently Used)&#xff0c;即最近最少使用&#xff0c;也称淘汰算法&#xff0c;在JDK中LinkedHashMap有相关实现 LRU的LinkedHashMap实现 LinkedHashMap继承HashMap。所以内存的存储结构和HashMap一样&#xff0c;但是LinkedH…

IDEA部署AI代写插件

前言 Hello大家好&#xff0c;当下是AI盛行的时代&#xff0c;好多好多东西在AI大模型的趋势下都变得非常的简单。 比如之前想画一幅风景画得先去采风&#xff0c;然后写实什么的&#xff0c;现在你只需描述出你想要的效果AI就能够根据你的描述在几分钟之内画出一幅你想要的风景…

27-压力测试

测试目标 & 测试数据 ● 测试目标 ○ 测试集群的读写性能 / 做集群容量规划 ○ 对 ES 配置参数进行修改&#xff0c;评估优化效果 ○ 修改 Mapping 和 Setting&#xff0c;对数据建模进行优化&#xff0c;并测试评估性能改进 ○ 测试 ES 新版本&#xff0c;结合实际场…

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明

4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明 文章目录 4. Spring Cloud Ribbon 实现“负载均衡”的详细配置说明前言1. Ribbon 介绍1.1 LB(Load Balance 负载均衡) 2. Ribbon 原理2.2 Ribbon 机制 3. Spring Cloud Ribbon 实现负载均衡算法-应用实例4. 总结&#x…

vue3【实战】切换全屏【组件封装】FullScreen.vue

效果预览 原理解析 使用 vueUse 里的 useFullscreen() 实现 代码实现 技术方案 vue3 vite UnoCSS vueUse 组件封装 src/components/FullScreen.vue <template><component:is"tag"click"toggle":class"[!isFullscreen ? i-ep:full-sc…

docker:基于Dockerfile镜像制作完整案例

目录 摘要目录结构介绍起始目录package目录target目录sh目录init.sh脚本start.sh脚本stop.sh脚本restart.sh脚本 config目录 步骤1、编写dockerfilescript.sh脚本 2、构件镜像查看镜像 3、保存镜像到本地服务器4、复制镜像文件到指定目录&#xff0c;并执行init.sh脚本5、查看挂…

微澜:用 OceanBase 搭建基于知识图谱的实时资讯流的应用实践

本文作者&#xff1a; 北京深鉴智源科技有限公司架构师 郑荣凯 本文整理自北京深鉴智源科技有限公司架构师郑荣凯&#xff0c;在《深入浅出 OceanBase 第四期》的分享。 知识图谱是一项综合性的系统工程&#xff0c;需要在在各种应用场景中向用户展示经过分页的一度关系。 微…

消息中间件分类

消息中间件&#xff08;Message Middleware&#xff09;是一种在分布式系统中实现跨平台、跨应用通信的软件架构。它基于消息传递机制&#xff0c;允许不同系统、不同编程语言的应用之间进行异步通信。 常见的消息中间件类型包括&#xff1a; 1. JMS&#xff08;Java Message S…

Mac上详细配置java开发环境和软件(更新中)

文章目录 概要JDK的配置JDK下载安装配置JDK环境变量文件 Idea的安装Mysql安装和配置Navicat Premium16.1安装安装Vscode安装和配置Maven配置本地仓库配置阿里云私服Idea集成Maven 概要 这里使用的是M3型片 14.6版本的Mac 用到的资源放在网盘 链接: https://pan.baidu.com/s/17…