最近做的需求需要实现在线预览ppt的功能,网上查了一遍,比较完美的方案都需要依赖第三方的服务或调用微软的在线接口,由于项目部署内网,同时为了不增加项目的复杂度,最终决定使用纯java实现,依赖 poi 将ppt 转成 png图片,然后再利用 itextpdf 将图片转成 pdf ,最后通过js实现预览。转换后的pdf效果图如下:
目前存在一个确定是 ppt字体全部统一换成了 宋体 ,以解决中文有些情况下乱码的问题,本来想研究下能不更换字体的方式,但是没时间,暂时就这样了。有解决方案的同学还请赐教。
下面直接上代码:
maven 依赖大致如下:
org.apache.poi
poi-ooxml
org.apache.poi
poi-scratchpad
4.1.0
org.apache.poi
poi-ooxml-schemas
4.1.0
org.apache.poi
ooxml-schemas
1.4
fr.opensagres.xdocreport
xdocreport
2.0.2
com.itextpdf
itextpdf
5.5.13.1
org.apache.pdfbox
fontbox
2.0.18
org.apache.pdfbox
pdfbox
2.0.18
com.itextpdf
itext-asian
5.2.0
org.apache.xmlgraphics
batik-bridge
1.12
net.coobird
thumbnailator
0.4.8
java 工具类
package com.fmi110.common.utils;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import com.ruoyi.common.exception.BusinessException;
import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import org.apache.poi.hslf.usermodel.*;
import org.apache.poi.xslf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author fmi110
* @description ppt 转图片;图片转pdf 工具
* @date 2021/8/19 20:16
*/
public class PPTUtil {
protected static final Logger log = LoggerFactory.getLogger(PPTUtil.class);
public static int width = 720;
public static int height = 540;
public static void PPTtoImage(String filePath, String fileName) throws Exception {
File f = new File(filePath + fileName);
if (f.exists()) {
if (f.getName().endsWith(".pptx") || f.getName().endsWith(".PPTX")) {
pptx2Image(filePath, fileName);
} else {
ppt2Image(filePath, fileName);
}
} else {
throw new BusinessException("文档不存在");
}
}
/**
* 将pptx转换为图片,直接保存在内存中,ppt很大时可能会oom!!!
*
* @param is ppt 输入流
* @return
* @throws IOException
*/
public static List pptx2Image(InputStream is) throws IOException {
// 获取系统可用字体
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = e.getAvailableFontFamilyNames();
List availFonts = CollectionUtils.arrayToList(fontNames);
XMLSlideShow ppt = new XMLSlideShow(is);
Dimension pgsize = ppt.getPageSize();
int pageSize = ppt.getSlides().size();
List imgList = new ArrayList<>(pageSize);
log.info("ppt 总页数:{}, 尺寸: width={},height={}", pageSize, pgsize.width, pgsize.height);
for (int i = 0; i < pageSize; i++) {
//防止中文乱码
for (XSLFShape shape : ppt.getSlides().get(i).getShapes()) {
if (shape instanceof XSLFTextShape) {
XSLFTextShape tsh = (XSLFTextShape) shape;
for (XSLFTextParagraph p : tsh) {
for (XSLFTextRun r : p) {
String fontFamily = r.getFontFamily();
//log.info(">>>>>原始ppt字体:{}", fontFamily);
fontFamily = "宋体";
//log.info(">>>>>ppt字体修改为:{}", fontFamily);
r.setFontFamily(fontFamily);
// if (!availFonts.contains(fontFamily)) {
// fontFamily = "宋体";
// log.info(">>>>>ppt字体修改为:{}", fontFamily);
// r.setFontFamily(fontFamily);
// }
}
}
}
}
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
// clear the drawing area
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
// render
ppt.getSlides().get(i).draw(graphics);
// save the output
ByteArrayOutputStream out = new ByteArrayOutputStream();
javax.imageio.ImageIO.write(img, "png", out);
imgList.add(out.toByteArray());
IOUtils.closeQuietly(out);
}
if (is != null) {
IOUtils.closeQuietly(is);
}
return imgList;
}
/**
* 将pptx转成图片,保存在同一目录的image目录下
*
* @param filePath ppt文件路径
* @param fileName ppt 文件名
* @throws IOException
*/
public static void pptx2Image(String filePath, String fileName) throws Exception {
// 获取系统可用字体
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = e.getAvailableFontFamilyNames();
List availFonts = CollectionUtils.arrayToList(fontNames);
FileInputStream is = new FileInputStream(filePath + fileName);
XMLSlideShow ppt = new XMLSlideShow(is);
Dimension pgsize = ppt.getPageSize();
int pageSize = ppt.getSlides().size();
log.info("ppt 总页数:{}, 尺寸: width={},height={}", pageSize, pgsize.width, pgsize.height);
for (int i = 0; i < pageSize; i++) {
//防止中文乱码
for (XSLFShape shape : ppt.getSlides().get(i).getShapes()) {
if (shape instanceof XSLFTextShape) {
XSLFTextShape tsh = (XSLFTextShape) shape;
for (XSLFTextParagraph p : tsh) {
for (XSLFTextRun r : p) {
String fontFamily = r.getFontFamily();
//log.info(">>>>>原始ppt字体:{}", fontFamily);
fontFamily = "宋体";
//log.info(">>>>>ppt字体修改为:{}", fontFamily);
r.setFontFamily(fontFamily);
// if (!availFonts.contains(fontFamily)) {
// fontFamily = "宋体";
// log.info(">>>>>ppt字体修改为:{}", fontFamily);
// r.setFontFamily(fontFamily);
// }
}
}
}
}
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
// clear the drawing area
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
// render
ppt.getSlides().get(i).draw(graphics);
// save the output
String pptname = fileName.substring(0, fileName.lastIndexOf("."));
String newimgPath = filePath + "image/" + pptname + "/";
File imgPath = new File(newimgPath);
if (!imgPath.exists()) {//图片目录不存在则创建
imgPath.mkdirs();
}
String file = newimgPath + (i + 1) + ".png";
FileOutputStream out = new FileOutputStream(file);
javax.imageio.ImageIO.write(img, "png", out);
IOUtils.closeQuietly(out);
}
if (is != null) {
IOUtils.closeQuietly(is);
}
}
/**
* 将ppt转换为图片,直接保存在内存中,ppt很大时可能会oom!!!
*
* @param is ppt 输入流
* @return
* @throws IOException
*/
public static List ppt2Image(InputStream is) throws IOException {
// 获取系统可用字体
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = e.getAvailableFontFamilyNames();
List availFonts = CollectionUtils.arrayToList(fontNames);
HSLFSlideShow ppt = new HSLFSlideShow(new HSLFSlideShowImpl(is));
Dimension pgsize = ppt.getPageSize();
int pageSize = ppt.getSlides().size();
List imgList = new ArrayList<>(pageSize);
log.info("ppt 总页数:{}, 尺寸: width={},height={}", pageSize, pgsize.width, pgsize.height);
for (int i = 0; i < pageSize; i++) {
//防止中文乱码
for (HSLFShape shape : ppt.getSlides().get(i).getShapes()) {
if (shape instanceof HSLFTextShape) {
HSLFTextShape tsh = (HSLFTextShape) shape;
for (HSLFTextParagraph p : tsh) {
for (HSLFTextRun r : p) {
String fontFamily = r.getFontFamily();
//log.info(">>>>>原始ppt字体:{}", fontFamily);
fontFamily = "宋体";
//log.info(">>>>>ppt字体修改为:{}", fontFamily);
r.setFontFamily(fontFamily);
// if (!availFonts.contains(fontFamily)) {
// fontFamily = "宋体";
// log.info(">>>>>ppt字体修改为:{}", fontFamily);
// r.setFontFamily(fontFamily);
// }
}
}
}
}
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
// clear the drawing area
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
// render
ppt.getSlides().get(i).draw(graphics);
// save the output
ByteArrayOutputStream out = new ByteArrayOutputStream();
javax.imageio.ImageIO.write(img, "png", out);
imgList.add(out.toByteArray());
IOUtils.closeQuietly(out);
}
if (is != null) {
IOUtils.closeQuietly(is);
}
return imgList;
}
/**
* 将ppt转成图片,保存在同一目录的image目录下
*
* @param filePath ppt文件路径
* @param fileName ppt 文件名
* @throws IOException
*/
public static void ppt2Image(String filePath, String fileName) throws IOException {
// 获取系统可用字体
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = e.getAvailableFontFamilyNames();
List availFonts = CollectionUtils.arrayToList(fontNames);
HSLFSlideShow ppt = new HSLFSlideShow(new HSLFSlideShowImpl(filePath + fileName));
Dimension pgsize = ppt.getPageSize();
for (int i = 0; i < ppt.getSlides().size(); i++) {
//防止中文乱码
for (HSLFShape shape : ppt.getSlides().get(i).getShapes()) {
if (shape instanceof HSLFTextShape) {
HSLFTextShape tsh = (HSLFTextShape) shape;
for (HSLFTextParagraph p : tsh) {
for (HSLFTextRun r : p) {
String fontFamily = r.getFontFamily();
//log.info(">>>>>原始ppt字体:{}", fontFamily);
fontFamily = "宋体";
//log.info(">>>>>ppt字体修改为:{}", fontFamily);
r.setFontFamily(fontFamily);
// if (!availFonts.contains(fontFamily)) {
// fontFamily = "宋体";
// log.info(">>>>>ppt字体修改为:{}", fontFamily);
// r.setFontFamily(fontFamily);
// }
}
}
}
}
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
// clear the drawing area
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
// render
ppt.getSlides().get(i).draw(graphics);
// save the output
String pptname = fileName.substring(0, fileName.lastIndexOf("."));
String newimgPath = filePath + "image/" + pptname + "/";
File imgPath = new File(newimgPath);
if (!imgPath.exists()) {//图片目录不存在则创建
imgPath.mkdirs();
}
String file = newimgPath + (i + 1) + ".png";
FileOutputStream out = new FileOutputStream(file);
javax.imageio.ImageIO.write(img, "png", out);
IOUtils.closeQuietly(out);
//resizeImage(filename, filename, width, height);
}
}
/***
* 功能 :调整图片大小
* @param srcImgPath 原图片路径
* @param distImgPath 转换大小后图片路径
* @param width 转换后图片宽度
* @param height 转换后图片高度
*/
public static void resizeImage(String srcImgPath, String distImgPath,
int width, int height) throws IOException {
File srcFile = new File(srcImgPath);
Image srcImg = ImageIO.read(srcFile);
BufferedImage buffImg = null;
buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
buffImg.getGraphics().drawImage(
srcImg.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0,
0, null);
ImageIO.write(buffImg, "JPEG", new File(distImgPath));
}
/**
* 将图片转换成pdf
*
* @return
* @throws Exception
*/
public static byte[] img2PDF(List images) throws Exception {
// Document doc = new Document();
// Document doc = new Document(PageSize.A4, 0, 0, 36.0F, 36.0F);//普通a4
com.itextpdf.text.Rectangle pageSize = new com.itextpdf.text.Rectangle(0, 0, width, height); // 自定义页面大小
Document doc = new Document(pageSize, 0, 0, 0, 0);
log.info(">>>>A4尺寸:width={},height={}", pageSize.getWidth(), pageSize.getHeight());
ByteArrayOutputStream pdfOut = new ByteArrayOutputStream();
PdfWriter.getInstance(doc, pdfOut);
doc.open();
float scale = pageSize.getWidth() / width;
for (byte[] image : images) {
com.itextpdf.text.Image pic = com.itextpdf.text.Image.getInstance(image);
pic.setScaleToFitLineWhenOverflow(true);
// pic.scaleAbsolute(width, height);
pic.scaleToFit(pageSize.getWidth(), height * scale);
doc.add(pic);
}
doc.close();
byte[] pdf = pdfOut.toByteArray();
IOUtils.closeQuietly(pdfOut);
return pdf;
}
/**
* ppt转化为pdf
* @param is ppt 输入流
* @return pdf 字节文件
* @throws Exception
*/
public static byte[] ppt2PDF(InputStream is) throws Exception {
return img2PDF(ppt2Image(is));
}
/**
* pptx转化为pdf
* @param is pptx 输入流
* @return pdf 字节文件
* @throws Exception
*/
public static byte[] pptx2PDF(InputStream is) throws Exception {
return img2PDF(pptx2Image(is));
}
/**
* 图片转pdf
*
* @param img
* @param descfolder
* @return
* @throws Exception
*/
public static String img2PDF(String imagePath, BufferedImage img, String descfolder) throws Exception {
String pdfPath = "";
try {
//图片操作
com.itextpdf.text.Image image = null;
File file = new File(descfolder);
if (!file.exists()) {
file.mkdirs();
}
pdfPath = descfolder + "/" + System.currentTimeMillis() + ".pdf";
String type = imagePath.substring(imagePath.lastIndexOf(".") + 1);
Document doc = new Document(null, 0, 0, 0, 0);
//更换图片图层
BufferedImage bufferedImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
bufferedImage.getGraphics().drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
bufferedImage = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), null).filter(bufferedImage, null);
//图片流处理
doc.setPageSize(new com.itextpdf.text.Rectangle(bufferedImage.getWidth(), bufferedImage.getHeight()));
log.info(bufferedImage.getWidth() + "()()()()()" + bufferedImage.getHeight());
ByteArrayOutputStream out = new ByteArrayOutputStream();
boolean flag = ImageIO.write(bufferedImage, type, out);
byte[] b = out.toByteArray();
image = com.itextpdf.text.Image.getInstance(b);
//写入PDF
log.info("写入PDf:" + pdfPath);
FileOutputStream fos = new FileOutputStream(pdfPath);
PdfWriter.getInstance(doc, fos);
doc.open();
doc.add(image);
doc.close();
} catch (IOException e) {
e.printStackTrace();
} catch (BadElementException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
return pdfPath;
}
public static void mergePDF(String[] files, String desfolder, String mergeFileName) throws Exception {
PDFMergerUtility mergePdf = new PDFMergerUtility();
for (String file : files) {
if (file.toLowerCase().endsWith("pdf"))
mergePdf.addSource(file);
}
mergePdf.setDestinationFileName(desfolder + "/" + mergeFileName);
mergePdf.mergeDocuments();
log.info("merge over");
}
public static void main(String[] args) throws Exception {
// 读入PPT文件
String filePath = "D:/test/";
String fileName = "docker.pptx";
List images = pptx2Image(new FileInputStream(new File(filePath + fileName)));
// 输出图片
for (int i = 1; i <= images.size(); i++) {
String path = filePath + i + ".png";
FileOutputStream output = new FileOutputStream(path);
IOUtils.write(images.get(i-1), output);
IOUtils.closeQuietly(output);
}
// 输出pdf
byte[] pdf = pptx2PDF(new FileInputStream(new File(filePath + fileName)));
FileOutputStream out = new FileOutputStream("D:/test/docker.pdf");
IOUtils.write(pdf, out);
IOUtils.closeQuietly(out);
}
}
执行工具类main函数后测试的效果:
参考文章:
https://blog.csdn.net/huoji555/article/details/88339039
https://www.cnblogs.com/chenpi/p/5534595.html
https://zhanglf.blog.csdn.net/article/details/106853803?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-18.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-18.control