目录
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){
// 获取幻灯片 XSLFSlide
List<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范围range
CTNumDataSource numDataSource = ser.getVal();
CTAxDataSource catDataSource = ser.getCat();
// TODO cat 也可能是 numRef
long 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范围range
CTNumDataSource numDataSource = ser.getVal();
CTAxDataSource catDataSource = ser.getCat();
// TODO cat 也可能是 numRef
long 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所在的cell
currentRow.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 (索引),为新系列分配一个唯一的 id
CTUnsignedInt 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);
// cat
CTAxDataSource 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));
}
// val
for (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所在的cell
int 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 (索引),为新系列分配一个唯一的 id
CTUnsignedInt 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);
// cat
CTAxDataSource 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));
}
// val
for (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);
// 获取幻灯片 XSLFSlide
List<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();
}
}