Java中如何使用实现PPT模板内容填充(依赖POI)

举报
KevinQ 发表于 2024/10/15 17:34:32 2024/10/15
【摘要】 使用POI按照模板动态填充PPT

在最近的工作中,遇到动态填充PPT内容的需求,记录一下解决方案与实现代码。

需求

因为特殊的工作需要,用户需要生成指定格式的PPT,但是PPT的部分内容是动态生成的,因此,程序需要实现:给定PPT文件模板的情况下,动态地将相关内容填充到指定位置

比如,模板如下:
1728518617521.png

期望执行效果如下图:
Pasted image 20241010081519.png

废话不多说,看代码:(完整代码请直接拉到文章最后,有现成方法)

POI引用包

添加依赖

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->  
<dependency>  
	<groupId>org.apache.poi</groupId>  
	<artifactId>poi</artifactId>  
	<version>5.2.2</version>  
</dependency>  
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->  
<dependency>  
	<groupId>org.apache.poi</groupId>  
	<artifactId>poi-ooxml</artifactId>  
	<version>5.2.2</version>  
</dependency>

关键代码:

  1. 解构PPT,一个Slideshow就是一个PPT文件。通过传入模板的输入流,创建一个XMLSlideShow对象,代表要处理的 PPT 文件。
SlideShow slideShow = new XMLSlideShow(inputStream);
  1. 遍历每一页PPT:
    每页幻灯片通常由各种形状(如文本框、图片、图表等)组成。通过Slide对象,可以访问和操作幻灯片中的这些元素。
for (Object slideObj : slideShow.getSlides()) {  
	// 每个slide都是一页元素
	Slide slide = (Slide) slideObj;
}
  1. 遍历每个幻灯片中的各个元素:
	// 遍历幻灯片中的所有形状
	List<Shape> shapes = slide.getShapes();
	// 循环时可能删除,修改循环方式
	Iterator<Shape> iterator = shapes.iterator();
	while (iterator.hasNext()) {
		// Shape是一个抽象类,表示幻灯片中的一个形状对象。
		Shape shape = iterator.next();
		
	}
  1. 获取每个Shape中的文字,依次进行替换:
if (shape instanceof TextShape) {
    // 文本输入框、形状中的文字内容也会进入这个领域
    TextShape textShape = (TextShape) shape;
    String text = textShape.getText();
    for (Map.Entry<String, Object> entry : replacementMap.entrySet()) {
		// 这里是因为replaceAll会按照正则表达式进行匹配,将大括号{进行一下转义
        String key = entry.getKey().replace("{{", "\\{\\{");
        text = text.replaceAll(key, entry.getValue() != null ? entry.getValue().toString() : "");
    }
    textShape.setText(text);
} else if (shape instanceof XSLFTable) {
    // 表格
    XSLFTable table = (XSLFTable)shape;
    for (int row = 0; row < table.getNumberOfRows(); row++) {
        for (int col = 0; col < table.getNumberOfColumns(); col++) {
            XSLFTableCell cell = table.getCell(row, col);
            String cellText = cell.getText();
            for (Map.Entry<String, Object> entry : replacementMap.entrySet()) {
                String key = entry.getKey().replace("{{", "\\{\\{");
                cellText = cellText.replaceAll(key, entry.getValue() != null ? entry.getValue().toString() : "");
            }
            cell.setText(cellText);
        }
    }
} else {
	// 可以依次补充其他元素文字进行补充
}

其中文字替换部分代码为:

// entry.getKey() 形如"{{标题}}", entry.getValue()形式如"真正的标题"
// key 形如"\\{\\{标题}}",这是基于正则表达式转义的需要
// 原text 可能是形式如: "这里的标题是{{标题}}"
// 替换后,text = "这里的标题是真正的标题"
String key = entry.getKey().replace("{{", "\\{\\{");
text = text.replaceAll(key, entry.getValue() != null ? entry.getValue().toString() : "");
  1. 替换后输出流:
// 保存修改到输出流
slideShow.write(outputStream);
return new ByteArrayInputStream(outputStream.toByteArray());

完整代码:

/**
     * @param inputStream 模版ppt的输入流
     * @param replacementMap 需要替换的文字,格式为{ "{{标题}}": "真正的标题" }
     * @return 替换后的ppt流
     */
    public static InputStream replaceTextInPPT(InputStream inputStream, Map<String, Object> replacementMap) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            // 创建 SlideShow 对象
            SlideShow slideShow = new XMLSlideShow(inputStream);
            // 遍历所有幻灯片
            for (Object slideObj : slideShow.getSlides()) {
                Slide slide = (Slide) slideObj;
                // 遍历幻灯片中的所有形状
                List<Shape> shapes = slide.getShapes();
                // 循环时可能删除,修改循环方式
                Iterator<Shape> iterator = shapes.iterator();
                while (iterator.hasNext()) {
                    Shape shape = iterator.next();
                    if (shape instanceof TextShape) {
                        // 文本输入框、形状中的文字内容也会进入这个领域
                        TextShape textShape = (TextShape) shape;
                        String text = textShape.getText();
                        for (Map.Entry<String, Object> entry : replacementMap.entrySet()) {
		                    // 这里是因为replaceAll会按照正则表达式进行匹配,将大括号{进行一下转义
                            String key = entry.getKey().replace("{{", "\\{\\{");
                            text = text.replaceAll(key, entry.getValue() != null ? entry.getValue().toString() : "");
                        }
                        textShape.setText(text);
                    } else if (shape instanceof XSLFTable) {
                        XSLFTable table = (XSLFTable)shape;
                        // 表格
                        for (int row = 0; row < table.getNumberOfRows(); row++) {
                            for (int col = 0; col < table.getNumberOfColumns(); col++) {
                                XSLFTableCell cell = table.getCell(row, col);
                                String cellText = cell.getText();
                                for (Map.Entry<String, Object> entry : replacementMap.entrySet()) {
                                    String key = entry.getKey().replace("{{", "\\{\\{");
                                    cellText = cellText.replaceAll(key, entry.getValue() != null ? entry.getValue().toString() : "");
                                }
                                cell.setText(cellText);
                            }
                        }
                    } else if (shape instanceof XSLFAutoShape) {

                    }
                }
            }

            // 保存修改到输出流
            slideShow.write(outputStream);
            return new ByteArrayInputStream(outputStream.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。