1、需求:
有一个pptx格式的模板,共4页。前3页为简介,最后1页为结束语页。要在第3页后添加商品信息幻灯片,商品信息包括文字和图片。
2、解决办法及实现效果(蓝字超链接,能看到图片):
- 将模板分为两部分,前3页为 介绍.pptx,最后1页为 尾页.pptx
- 单独创立一页为商品模板.pptx,设置商品参数。左边空白区域为商品主图(1张)+详情图(3张)
- 商品信息填充到商品模板.pptx
- 填充后的商品模板拼接到介绍.pptx后,再将尾页.pptx拼接到最后,最终得到效果.pptx
3、代码逻辑:
1、maven导入:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
2、商品信息,商品属性中详情图为3张(,分隔,可通过http下载),图片格式限定为jpg或png。
package com.ruoyi.web.controller.operatePpt;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @program:test-item
* @author:
* @Time: 2023/12/25 16:15
* @description: 商品信息
* @Version 1.0
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Product implements Serializable
{
private static final long serialVersionUID = 1L;
/** 商品名称 */
private String name;
/** 商品类型 */
private String type;
/** 商品品牌 */
private String brand;
/** 商品价格 */
private String price;
/** 商品分类 */
private String classify;
/** 商品主图 */
private String mainImage;
/** 商品详情图 */
private String detailImage;
public Product(String name, String type, String brand, String price, String classify) {
this.name = name;
this.type = type;
this.brand = brand;
this.price = price;
this.classify = classify;
this.mainImage = "https://xxx_20231214160603A097.jpg";
this.detailImage = "https://xxx_20231214160603A097.jpg,https://xxx_20231214160603A097.jpg,https://xxx_20231214160603A097.jpg";
}
}
3、文件位置自定义,暂时放在resources/static下
4、处理ppt。我这里添加两张商品ppt,主要是对商品信息对ppt中参数替换。下载图片临时路径、最终文件路径、图片大小及摆放位置自定义
package com.ruoyi.web.controller.operatePpt;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.http.HttpUtil;
import org.apache.poi.common.usermodel.fonts.FontGroup;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.xslf.usermodel.*;
import org.springframework.core.io.ClassPathResource;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.List;
/**
* @program:test-item
* @author:
* @Time: 2023/12/22 15:59
* @description: 处理ppt
* @Version 1.0
*/
public class OperatePPT {
public static void main(String[] args) {
XMLSlideShow ppt = null;
XMLSlideShow trailerPagePpt = null;
try {
List<Product> products = new ArrayList<>();
Product product2 = new Product("键盘", "980", "腹灵", "899", "办公用品");
Product product = new Product("鼠标", "502", "罗技", "599", "办公用品");
products.add(product2);
products.add(product);
// 读取模板
ClassPathResource pptTemplatePathResource = new ClassPathResource("static/介绍.pptx");
ppt = new XMLSlideShow(pptTemplatePathResource.getInputStream());
// 读取商品模板
ClassPathResource productTemplatePathResource = new ClassPathResource("static/商品模板.pptx");
for (Product p : products) {
try {
splicingProductTemplate(productTemplatePathResource, p, ppt);
} catch (IOException e) {
throw new RuntimeException("填充商品信息异常", e);
}
}
// 读取尾页
ClassPathResource trailerPageTemplatePathResource = new ClassPathResource("static/尾页.pptx");
trailerPagePpt = new XMLSlideShow(trailerPageTemplatePathResource.getInputStream());
// 拼接尾页数据
for (XSLFSlide slide : trailerPagePpt.getSlides()) {
XSLFSlide pptSlide = ppt.createSlide(slide.getSlideLayout());
pptSlide.importContent(slide);
}
// 输出结果文件
ppt.write(Files.newOutputStream(Paths.get("C:\\Users\\admin\\Desktop\\all.pptx")));
} catch (Exception e) {
throw new RuntimeException("生成ppt异常", e);
} finally {
IoUtil.close(trailerPagePpt);
IoUtil.close(ppt);
}
}
private static void splicingProductTemplate(ClassPathResource productTemplatePathResource, Product product, XMLSlideShow ppt) throws IOException {
// 重新加载商品ppt模板信息
XMLSlideShow productPpt = new XMLSlideShow(productTemplatePathResource.getInputStream());
// 处理商品模板,添加商品信息
readAndProcessPowerPointTemplate(productPpt, product);
// 拼接商品模板数据
for (XSLFSlide slide : productPpt.getSlides()) {
XSLFSlide pptSlide = ppt.createSlide(slide.getSlideLayout());
pptSlide.importContent(slide);
}
}
/**
* 读取PPT文件,并对其进行处理
*
* @param ppt
* @param product
*/
private static void readAndProcessPowerPointTemplate(XMLSlideShow ppt, Product product) {
List<XSLFSlide> slides = ppt.getSlides();
for (XSLFSlide slide : slides) {
processSlide(slide, ppt, product);
}
}
/**
* 处理文本
*
* @param slide
* @param ppt
* @param product
*/
private static void processSlide(XSLFSlide slide, XMLSlideShow ppt, Product product) {
List<XSLFShape> shapes = slide.getShapes();
// 创建Map对象
Map<String, String> map = new HashMap<>();
// 将需要被替换和用于替换的文本以键值的形式添加到Map
map.put("${name}", product.getName());
map.put("${brand}", product.getBrand());
map.put("${type}", product.getType());
map.put("${price}", product.getPrice());
map.put("${classify}", product.getClassify());
for (XSLFShape shape : shapes) {
if (shape instanceof XSLFTextShape) {
processTextShape((XSLFTextShape) shape, map);
}
}
// 图片地址
List<String> urls = new ArrayList<>();
urls.add(product.getMainImage());
urls.addAll(Arrays.asList(product.getDetailImage().split(",")));
for (int i = 0; i < urls.size(); i++) {
XSLFPictureShape pictureShape = addPicturesToSlide(slide, urls.get(i), ppt);
setPictureAnchor(pictureShape, i);
}
}
/**
* 处理文本框
*/
private static void processTextShape(XSLFTextShape shape, Map<String, String> map) {
if (shape != null) {
// 若想对文本框内的文字进行更改,还需要进行如下步骤
List<XSLFTextParagraph> textParagraphs = shape.getTextParagraphs();
for (XSLFTextParagraph tp : textParagraphs) {
List<XSLFTextRun> textRuns = tp.getTextRuns();
for (XSLFTextRun r : textRuns) {
for (String key : map.keySet()) {
if (r.getRawText().contains(key)) {
replaceText(r, map.get(key), 20.0, "宋体", Color.BLACK);
}
}
}
}
}
}
/**
* 替换文本,并设置字体和颜色
*
* @param text
* @param fontSize
* @param fontName
* @param color
*/
private static void replaceText(XSLFTextRun r, String text, double fontSize, String fontName, Color color) {
r.setText(text);
r.setFontSize(fontSize);
r.setFontFamily(fontName, FontGroup.EAST_ASIAN);
r.setFontColor(color);
}
/**
* 添加图片到PPT中
*/
private static XSLFPictureShape addPicturesToSlide(XSLFSlide slide, String url, XMLSlideShow ppt) {
String fileName = FileUtil.getName(url);
String suffix = FileUtil.getSuffix(url);
Map<String, PictureData.PictureType> typeMap = new HashMap<>();
typeMap.put("jpg", PictureData.PictureType.JPEG);
typeMap.put("png", PictureData.PictureType.PNG);
if (typeMap.get(suffix.toLowerCase()) == null) {
throw new RuntimeException("图片格式不符合要求");
}
// 下载图片文件并保存到临时目录
File file = HttpUtil.downloadFileFromUrl(url, "C:\\Users\\admin\\Desktop\\" + fileName);
// 图片文件输入流
FileInputStream imageFis = null;
try {
imageFis = new FileInputStream(file);
// 获取图片大小
int len = (int) file.length();
// 创建一个字节数组,数组大小与图片文件大小一致
byte[] imageData = new byte[len];
// 将图片数据读进字节数组中
imageFis.read(imageData);
// 将图片添加到PPT中
XSLFPictureData pd = ppt.addPicture(imageData, typeMap.get(suffix.toLowerCase()));
return slide.createPicture(pd);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
IoUtil.close(imageFis);
FileUtil.del(file);
}
}
/**
* 设置图片框位置
*/
private static void setPictureAnchor(XSLFPictureShape pictureShape, int index) {
if (pictureShape != null) {
// x,y为图片左上角坐标偏移
int x = 0, y = 0;
switch (index) {
case 0:
x = 40;
y = 35;
break;
case 1:
x = 300;
y = 35;
break;
case 2:
x = 40;
y = 245;
break;
case 3:
x = 300;
y = 245;
break;
default:
break;
}
// 设置图片框的位置和大小
pictureShape.setAnchor(new Rectangle(x, y, 260, 210));
}
}
}