使用poi将商品信息填充到商品模板,并将填充后的幻灯片拼接到总模板

1、需求:

        有一个pptx格式的模板,共4页。前3页为简介,最后1页为结束语页。要在第3页后添加商品信息幻灯片,商品信息包括文字和图片。

2、解决办法及实现效果(蓝字超链接,能看到图片):

  1. 将模板分为两部分,前3页为 介绍.pptx,最后1页为 尾页.pptx        
  2. 单独创立一页为商品模板.pptx,设置商品参数。左边空白区域为商品主图(1张)+详情图(3张)
  3. 商品信息填充到商品模板.pptx
  4. 填充后的商品模板拼接到介绍.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));
        }
    }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值