Java输出PPT文件(一) - 合并PPT
0. 前言
最近做了一个业务需求,存在已设计好的PPT文件,并且数据用占位符标识,需要将占位符替换为实际数据,然后将需要的PPT合并在一起,输出一个完整的PPT文件。
这个是笔者的同事提前去做了预研的,但真正投入开发的时候还是遇到了各种问题。笔者自己搜索是找到了一个收费的框架,如果使用免费的版本,输出文件会有水印,所以只能直接放弃,还是用回POI。
将使用过程记录下。
1. 依赖
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-full -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-full</artifactId>
<version>5.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-schemas -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/ooxml-schemas -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.4</version>
</dependency>
注意:poi-ooxml、poi-ooxml-full目前最高版本是5.2.3,但需要Apache的commons-io也为高版本,所以这里使用了5.0.0,想试用5.2.3的朋友先解决下依赖问题,笔者遇到的报错如下:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream
2. 代码
PowerPoint工具测试类:
import org.apache.poi.xslf.usermodel.*;
import org.springframework.util.CollectionUtils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Copyright: Horizon
*
* @ClassName PowerPointUtilTest
* @Description PowerPoint工具测试类
* @Author Nile (QQEmail:576109623)
* @Date 15:48 2022/11/5
* @Version 1.0.0
*/
public class PowerPointUtilTest {
public static void main(String[] args) throws IOException {
// 文件路径及文件名称
String rootDir = "src/main/resources/ppt/";
String[] pptArray = {"Title.pptx", "Foreword.pptx", "Dependency.pptx"};
// 合并PPT
mergePPT(rootDir, Arrays.asList(pptArray));
}
/**
* 合并PPT
* @Author Nile (QQEmail:576109623)
* @Date 22:18 2022/11/13
* @param rootDir 文件路径
* @param fileNameList 文件名称列表
* @return void
*/
private static void mergePPT(String rootDir, List<String> fileNameList) throws IOException {
if (CollectionUtils.isEmpty(fileNameList)) {
return;
}
// 1. 使用第1个PPT作为基础文件(不建议创建空对象,然后从第1个开始合并操作)
XMLSlideShow ppt = new XMLSlideShow(new FileInputStream(rootDir + fileNameList.get(0)));
// 2. 从第2个文件开始遍历,合并
for (int i = 1; i < fileNameList.size(); i++) {
FileInputStream inputstream = new FileInputStream(rootDir + fileNameList.get(i));
XMLSlideShow src = new XMLSlideShow(inputstream);
// 遍历每张幻灯片
for (XSLFSlide srcSlide : src.getSlides()) {
// 合并
ppt.createSlide().importContent(srcSlide);
}
}
// 3. 输出
String resultName = "Result.pptx";
FileOutputStream out = new FileOutputStream(rootDir + resultName);
ppt.write(out);
out.close();
}
}
3. 测试
3.1 模板准备
建立几张PPT,用于合并测试。
3.2 合并结果
4. 问题
从测试结果看是不是很不错,但其实过程遇到了几个问题。
4.1 母版问题
笔者一开始也是根据网上的建议(章节5中链接1),先创建一个空的XMLSlideShow
,然后从第1个文件开始合并操作,但这会出现一个问题,母版全部丢失。
以章节3的测试为例,合并后母版没有了,只剩下白底和文字,而且文字的位置都跑到幻灯片外边去了。(然后笔者回去看了章节5中链接1的案例也是如此,好吧😥)
而在工作中的开发,笔者拿业务需求的PPT操作,合并后连长宽比都改变了。。。
因此,强烈建议使用第1个文件作为基础,然后从第2个文件开始合并操作!!!
4.2 版式问题
先说明,笔者并不知道怎么解决,待研究。。😅
一开始版式选择的是标题和内容。
然后合并后:
右键查看了下,除了第1页幻灯片,从第2页开始,版式都变成了空白,内容全部置顶和置左。
笔者不知道POI是否有开放API用于获取PPT的版式和设置版式,但笨方法就是,把原始文件的版式全部设置为空白,然后手动调整标题和内容的位置。合并后的效果如章节3。