java实现PDF文件生成以及合并

需求:根据给定的ppt模板,由后端整合数据,输出一份新的ppt。其实就是将ppt模板中的占位符替换成需要的数据,例如图片、文本等,然后再调整下样式,合并每一页成一份完整的ppt即可。

使用依赖以及相关包

import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-slides</artifactId>
    <version>21.11</version> <!-- 版本号根据实际情况调整 -->
</dependency>

准备工作:一些ppt,将其中需要替换的数据设置成,插入文本框,根据你的喜好设置占位符,我的是:#{},如下

插入文本框是因为这个比较好处理,当然也可以用其他形状,到时候遍历每一页的ppt,找到符合的形状,替换即可。

以下是关键源码

两个实体类,一个是文本对象,一个是图片对象,包含了各自的属性,用来调整样式



@Data
public class PptStrRequest {
    /**
     * 文本
     */
    private String strValue;
    /**
     * 字号
     */
    private Float strFont;
    /**
     * 颜色
     */
    private Color strColor;

    public PptStrRequest(String strValue, Float strFont, Color strColor) {
        this.strValue = strValue;
        this.strFont = strFont;
        this.strColor = strColor;
    }
}

@Data
public class PptImgRequest {
    /**
     * 图片路径
     */
    private String imgUrl;
    /**
     *  矩形的左上角 x 坐标
     */
    private Float imgLeftX;
    /**
     * 矩形的左上角 y 坐标
     */
    private Float imgLeftY;
    /**
     * 矩形的宽度
     */
    private Float imgW;
    /**
     * 矩形的高度。
     */
    private Float imgH;

    public PptImgRequest(String imgUrl, Float imgLeftX, Float imgLeftY, Float imgW, Float imgH) {
        this.imgUrl = imgUrl;
        this.imgLeftX = imgLeftX;
        this.imgLeftY = imgLeftY;
        this.imgW = imgW;
        this.imgH = imgH;
    }
}
params 用来存储数据,在PPT进行数据替换时使用,可以调用自己需要的接口,
Color类构造对象直接穿RGB值,当然还有其他方法,按自己需要使用

        HashMap<String, Object> params = new HashMap<>();
        params.put("1_str1", new PptStrRequest(formatter.format(LocalDateTime.now()), 15f, new Color(68, 114, 196)));

        Map<String, String> page2 = pptDataService.getRainfallForecast();
        params.put("2_str1", new PptStrRequest(page2.get("2_str1"), 20f, new Color(68, 114, 196)));
        params.put("2_str2", new PptStrRequest(page2.get("2_str2"), 15f, new Color(0, 176, 240)));
        params.put("2_img1", new PptImgRequest(page2.get("2_img1"), 300f, 100f, 420f, 360f));
        params.put("2_img2", new PptImgRequest("/pic/2_img2.png", 520f, 440f, 170f, 13f));

包装好数据后,就进行PPT操作了,总的做法是:先加载整个PPT文件,然后遍历每一页,每一页再遍历所有形状,找到自己需要替换数据的形状,替换成相应的文本或者图片,在调整样式,例如相对位置、大小、颜色等

我的是每一个PPT文件就设置成一页,后期随意页码自由组合成一份新的PPT文件。

上源码

// 多个PPT模板
List<String> ppts = Arrays.asList("upload/ppt0","upload/ppt1","upload/ppt2","upload/ppt3");
for (int i = 0; i < ppts.size(); i++) {
	String outputPath = pptPath + "/out/ppt" + i + ".pptx";
	if (ppts.get(i).contains("ppt0")) {
		continue;
	}
	Presentation presentation = new Presentation(ppts.get(i));
//             遍历每一页
	for (int j = 0; j < presentation.getSlides().size(); j++) {
//                 替换占位符
		try {
			replacePlaceholders(presentation, params);
		} catch (IOException e) {
			e.printStackTrace();
		}
		presentation.save(outputPath, com.aspose.slides.SaveFormat.Pptx);
	}
}

// 占位符替换
private void replacePlaceholders(Presentation presentation, HashMap<String, Object> params) throws IOException {
	for (ISlide slide : presentation.getSlides()) {
		for (int i = 0; i < slide.getShapes().size(); i++) {
			IShape shape = slide.getShapes().get_Item(i);
			// 处理文本框
			if (shape instanceof AutoShape) {
				AutoShape autoShape = (AutoShape) shape;
				// 替换文本
				if (autoShape.getTextFrame() != null) {
					String replaceHolder = autoShape.getTextFrame().getText();
					if (replaceHolder.startsWith("#{") && replaceHolder.endsWith("}")) {
						Iterator var6 = params.entrySet().iterator();
						while (var6.hasNext()) {
							Map.Entry<String, Object> entry = (Map.Entry) var6.next();
							if (StringUtils.isEmpty(entry.getKey())) {
								continue;
							}
							String key = entry.getKey();
							String subKey = "#{" + key + "}";
							Object obj = params.get(key);
							if (subKey.contains("str") && replaceHolder.equals(subKey)) {
								PptStrRequest entity = (PptStrRequest) obj;
								String tx = entity.getStrValue();
								if (StringUtils.isEmpty(tx)) {
									tx = "无";
								}
								autoShape.getTextFrame().setText((tx));
								// 设置文本字体、颜色、字号
								IPortionFormat portionFormat = autoShape.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat();
								portionFormat.setFontHeight(entity.getStrFont());
//                                    portionFormat.getFillFormat().setFillType(FillType.Solid);
								portionFormat.getFillFormat().getSolidFillColor().setColor(entity.getStrColor());
							} else if (subKey.contains("img") && replaceHolder.equals(subKey)) {
								PptImgRequest entity = (PptImgRequest) obj;
								((AutoShape) shape).getTextFrame().setText(""); // 清空文本内容
								String url = entity.getImgUrl();
								if (StringUtils.isEmpty(url) || !new File(url).exists()) {
									url = pptPath + "/pic/bg.png";
								}
								IPPImage image = presentation.getImages().addImage(new FileInputStream(url));
								// 设置宽高以及位置
								slide.getShapes().addPictureFrame(ShapeType.Rectangle, entity.getImgLeftX(),
										entity.getImgLeftY(), entity.getImgW(), entity.getImgH(), image);
								shape.getFillFormat().setFillType(com.aspose.slides.FillType.Picture);

							}
						}
					}
				}
			}
		}
	}
}

处理完每个PPT文件后,进行合并成一份

public void mergePPTs(String filePath) throws IOException {
    // 模板中的第一份PPT是一个空白的,将输出的PPT合并到这个空白的PPT	
List<String> modelPpt = Arrays.asList("","","");
    // 替换完厚的PPT
	List<String> outPpt = Arrays.asList("","","");
	// 空白ppt模板
	if (CollectionUtil.isEmpty(modelPpt)) {
		throw new BindException("ppt模板为空!");
	}
	Presentation pres1 = new Presentation(modelPpt.get(0));
	try {
		for (int i = 0; i < outPpt.size(); i++) {
			Presentation temp = new Presentation(outPpt.get(i));
			try {
				for (ISlide slide : temp.getSlides()) {
					// 此处易报错,采用这种方式不会
					pres1.getSlides().addClone(slide);
				}
			} finally {
				if (temp != null) {
					temp.dispose();
				}
			}
		}
		pres1.save(filePath, SaveFormat.Pptx);
	} finally {
		if (pres1 != null) {
			pres1.dispose();
		}
	}
}

有问题的合并方式如下

// 合并不完全,样式错乱
for (String pptFile : pptFiles) {
	Presentation ppt = new Presentation(pptFile);
	mergedPPT.getSlides().insertClone(mergedPPT.getSlides().size(), ppt.getSlides().get_Item(0));
	ppt.dispose();
}

mergedPPT.save(outputFileName, SaveFormat.Pptx);
mergedPPT.dispose();


// 报错  importContent 方法会爆 Currently only SolidPaint is supported!
public static void mergedPresentation1() throws IOException {
	List<String> pptFiles = Arrays.asList("template/ppt/out/ppt1.pptx", "template/ppt/out/ppt2.pptx");
	XMLSlideShow mergedPPT = new XMLSlideShow();

	for (String pptFile : pptFiles) {
		XMLSlideShow ppt = new XMLSlideShow(new FileInputStream(pptFile));

		for (XSLFSlide slide : ppt.getSlides()) {
			XSLFSlide newSlide = mergedPPT.createSlide();

			for (XSLFShape shape : slide.getShapes()) {
				XSLFFill fill = shape.getFill();
				if (!(fill instanceof XSLFSolidPaint)) {
					// 如果填充不是SolidPaint,则替换为SolidPaint
					shape.setFill(new XSLFSolidPaint());
				}
				newSlide.importContent(shape);
			}
		}

		ppt.close();
	}

	FileOutputStream out = new FileOutputStream("merged.pptx");
	mergedPPT.write(out);
	out.close();
	mergedPPT.close();
}

aspose-slides 开发文档地址:

Aspose.Slides for Java|Aspose.Slides Documentation

还是看人家的,网上其余的一大堆都不能使用,会出各种奇怪的问题

生成PPT文档有些耗时,可以加个异步处理,先返回给前端数据

AsynCaller.Run(() -> {
    // PPT生成
});

------------------------------------------------------割了---------------------------------------------------------------------今天稍微做了下优化,被吐槽说之前那样太麻烦了,还要一个个调,确实是哦。

1.不再使用实体类控制文本样式,在PPT模板上设置好,直接替换文本,更加便捷

2.不再使用文本框替换图片的方式,直接在模板上设置一个默认的底图,调好位置和宽高,在替换的时候,先获取底图的x、y、w、h四个参数,然后直接隐藏底图,把新的图片插入并且沿用原来的样式。

主要是  replacePlaceholders方法做了调整

private void replacePlaceholders(Presentation presentation, HashMap<String, String> params) throws IOException {
	for (ISlide slide : presentation.getSlides()) {
		for (int i = 0; i < slide.getShapes().size(); i++) {
			IShape shape = slide.getShapes().get_Item(i);
			if (shape instanceof AutoShape) {
				AutoShape autoShape = (AutoShape) shape;
				// 替换文本
				if (autoShape.getTextFrame() != null) {
					String replaceHolder = autoShape.getTextFrame().getText();
					if (replaceHolder.startsWith("#{") && replaceHolder.endsWith("}")) {
						Iterator var6 = params.entrySet().iterator();
						while (var6.hasNext()) {
							Map.Entry<String, Object> entry = (Map.Entry) var6.next();
							if (StringUtils.isEmpty(entry.getKey())) {
								continue;
							}
							String key = entry.getKey();
							String subKey = "#{" + key + "}";
							if (subKey.contains("str") && replaceHolder.equals(subKey)) {
								String tx = params.get(key);
								if (StringUtils.isEmpty(tx)) {
									tx = "无";
								}
								autoShape.getTextFrame().setText((tx));
							}
						}
					}
				}
			} else if (shape instanceof IPictureFrame) {
			// 图片形状处理
				Iterator var6 = params.entrySet().iterator();
				while (var6.hasNext()) {
					Map.Entry<String, Object> entry = (Map.Entry) var6.next();
					// 在PPT模板中设置好需要替换图片的名字,入参时就用这个名字作为key
                    // params.put("我是老王", page2.get("2_img1"));					
                    if (StringUtils.isEmpty(entry.getKey())
                     || !shape.getName().equals(entry.getKey())) {
						continue;
					}
					String url = params.get(entry.getKey());
					if (StringUtils.isEmpty(url) || !new File(url).exists()) {
						url = pptPath + "/pic/bg.png";
					}
					// 将原来的形状隐藏
					shape.setHidden(true);
					// 获取新的图片
					IPPImage image = presentation.getImages().addImage(new FileInputStream(url));
					IShapeCollection collection = slide.getShapes();
					// 沿用原先的布局
					collection.addPictureFrame(ShapeType.Rectangle, shape.getX(), shape.getY(), shape.getWidth(), shape.getHeight(), image);
				}
			}
		}
	}
}

shape的处理类型有这些,按需处理

if (shape instanceof IAutoShape) {
    // 处理自动形状
    IAutoShape autoShape = (IAutoShape) shape;
    System.out.println("ppt元素类型"+"IAutoShape");
    // 进行自动形状的操作
} else if (shape instanceof IGroupShape) {
    // 处理组合形状
    IGroupShape groupShape = (IGroupShape) shape;
    System.out.println("ppt元素类型"+"IGroupShape");
    // 进行组合形状的操作
} else if (shape instanceof IPictureFrame) {
    // 处理图片框
    IPictureFrame pictureFrame = (IPictureFrame) shape;
    System.out.println("ppt元素类型"+"IPictureFrame");
    // 进行图片框的操作
} else if (shape instanceof ITable) {
    // 处理表格
    ITable table = (ITable) shape;
    System.out.println("ppt元素类型"+"ITable");
    // 进行表格的操作
} else if (shape instanceof ITextFrame) {
    // 处理文本框
    ITextFrame textFrame = (ITextFrame) shape;
    System.out.println("ppt元素类型"+"ITextFrame");
    // 进行文本框的操作
} else {
    // 其他类型的形状,根据实际情况进行处理
    System.out.println("ppt元素类型"+"其他");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值