PPT转图片或者是PDF。也许是日常开发中一个并不常见的需求,网上的技术也五花八门,搜寻一番下自己做了个总结,作者最终使用的是openoffice的方案,当然poi的方案也很优秀
方案1:Spire.Presentation
优点: 速度快、精准,代码简洁、转换的图片无乱码
缺点:不开源,超级贵、免费状态下只能转10页 (我是在web状态下发请求转换的,发现一个请求可以转10页,不知道如果多个线程或者是多次求情控制每次转10页这种方案是否可行)
官网: https://www.e-iceblue.cn (价格很高 )
1. 首先引入maven依赖(这里是免费版)
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.presentation.free</artifactId>
<version>2.6.1</version>
</dependency>
// ppt -> pdf
public static void main(String[] args) throws Exception {
//create a Presentataion instance
Presentation presentation = new Presentation();
//load the sample PowerPoint file
presentation.loadFromFile("C:/Users/Administrator/Desktop/template.pptx");
//save to PDF file
presentation.saveToFile("ToPDF.pdf", FileFormat.PDF);
presentation.dispose();
}
// ppt -> 图片
public static void main(String[] args) throws Exception {
//create a Presentation object
Presentation presentation = new Presentation();
//load an example PPTX file
presentation.loadFromFile("C:/Users/Administrator/Desktop/template.pptx");
//loop through the slides
for (int i = 0; i < presentation.getSlides().getCount(); i++) {
//save each slide as a BufferedImage
BufferedImage image = presentation.getSlides().get(i).saveAsImage();
//save BufferedImage as PNG file format
String fileName = String.format("ToImage-%1$s.png", i);
ImageIO.write(image, "PNG",new File(fileName));
}
presentation.dispose();
}
方案2:使用POI的ppt工具
优点:开源免费
缺点:转换后图片模糊、部分ppt样式无法转换、部分会出现ppt大小与图片大小不符的问题,另外还存在ppt文字乱码问题、使用该工具pptx的转换效果优于ppt
1.引入maven依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.2</version>
</dependency>
2.编写代码,2003版本的ppt(后缀是.ppt)、和2007版本的pptx(后缀是.pptx)转换方法不同
/**
* poi 完成ppt->图片 (可以做进度、图片是一张一张的转换的)
*
* @author: Ming
* @date: 2020/12/7
*/
@Slf4j
public class PoiPPT2Pic {
/**
* ppt/pptx转图片
*
* @param pptFile ppt文件
* @param saveFolder 要转换的图片保存的文件夹
* @return List集合图片在服务器的绝对路径位置
* @throws Exception 转换异常
*/
public static List<String> pptConver2Png(File pptFile, File saveFolder) throws Exception {
// 获取后缀并校验
String suffix = pptFile.getName().substring(pptFile.getName().lastIndexOf(".") + 1);
if (!"ppt".equals(suffix) && !"pptx".equals(suffix)) {
log.error("文件格式异常,非ppt,pptx");
throw new RuntimeException("文件格式异常,非ppt,pptx");
}
// 调用指定方法
return "ppt".equals(suffix) ? PPT2Pic.ppt2Png(pptFile, saveFolder) : PPT2Pic.pptx2Png(pptFile, saveFolder);
}
/**
* ppt 转图片
*
* @param pptFile ppt文件
* @param saveFolder 要转换的图片保存的文件夹
* @return List集合图片在服务器的绝对路径位置
* @throws Exception 转换异常
*/
public static List<String> ppt2Png(File pptFile, File saveFolder) throws Exception {
log.info("=>ppt路径为:" + pptFile.getAbsolutePath());
log.info("=>要存储的路径为:" + saveFolder.getAbsolutePath());
if (!pptFile.exists()) {
log.error("ppt文件不存在");
return Collections.emptyList();
}
// 路径不存在则创建
if (!saveFolder.exists()) {
saveFolder.mkdirs();
}
// 指定图片保存的路径
List<String> pngFileList = new ArrayList<String>();
// 开始时间
long startTime = System.currentTimeMillis();
FileInputStream is = null;
// 将ppt文件转换成每一帧的图片
HSLFSlideShow ppt = null;
try {
ZipSecureFile.setMinInflateRatio(-1.0d);
is = new FileInputStream(pptFile);
ppt = new HSLFSlideShow(is);
// 指定index
int idx = 1;
Dimension pageSize = ppt.getPageSize();
// 缩放比例 3倍以下会迷糊
double image_rate = 4.0;
// 获取高度宽度
int imageWidth = (int) Math.floor(image_rate * pageSize.getWidth());
int imageHeight = (int) Math.floor(image_rate * pageSize.getHeight());
// 循环获取图片
log.info("=>ppt解析完毕共:" + ppt.getSlides().size() + '张');
for (HSLFSlide slide : ppt.getSlides()) {
// 构造输出流和画布
BufferedImage img = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = img.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// 设置底色
graphics.setPaint(Color.white);
// 绘制区域与缩放比例
graphics.fill(new Rectangle2D.Float(0, 0, imageWidth, imageHeight));
graphics.scale(image_rate, image_rate);
//防止中文乱码 (这里可以为黑体或者宋体)--- 即使制定了字体也会出现部分字体或艺术字乱码的现象
for (HSLFShape shape : slide.getShapes()) {
if (shape instanceof HSLFTextShape) {
HSLFTextShape hslfTextShape = (HSLFTextShape) shape;
for (HSLFTextParagraph hslfTextParagraph : hslfTextShape) {
for (HSLFTextRun hslfTextRun : hslfTextParagraph) {
hslfTextRun.setFontFamily("黑体");
}
}
}
}
// 指定输出流
FileOutputStream out = null;
try {
// 绘制
slide.draw(graphics);
File pngFile = new File(saveFolder.getAbsolutePath() + String.format("/%d.png", idx++));
out = new FileOutputStream(pngFile);
// 输出图片
ImageIO.write(img, "png", out);
log.info("=>第" + (idx - 1) + "张,存储路径:" + pngFile.getAbsolutePath());
pngFileList.add(pngFile.getAbsolutePath());
} finally {
try {
if (out != null) {
out.flush();
out.close();
}
if (graphics != null) {
graphics.dispose();
}
if (img != null) {
img.flush();
}
} catch (IOException e) {
log.error("=>ppt转换流关闭异常", e);
}
}
}
} finally {
try {
if (is != null) {
is.close();
}
if (ppt != null) {
ppt.close();
}
} catch (Exception e) {
log.error("=>ppt转换流关闭异常", e);
}
}
log.info("=>ppt转换完成,共用时" + ((System.currentTimeMillis() - startTime) / 1000) + "秒");
return pngFileList;
}
/**
* pptx 转换到png
*
* @param pptxFile ppt文件
* @param saveFolder 图片保存的文件按夹
* @return List集合图片在服务器的绝对路径位置
* @throws Exception 转换异常
*/
public static List<String> pptx2Png(File pptxFile, File saveFolder) throws Exception {
log.info("=>ppt路径为:" + pptxFile.getAbsolutePath());
log.info("=>要存储的路径为:" + saveFolder.getAbsolutePath());
if (!pptxFile.exists()) {
log.error("pptx文件不存在");
return Collections.emptyList();
}
// 路径不存在则创建
if (!saveFolder.exists()) {
saveFolder.mkdirs();
}
List<String> pngFileList = new ArrayList<>();
long startTime = System.currentTimeMillis();
FileInputStream is = null;
// 将ppt文件转换成每一帧的图片
XMLSlideShow pptx = null;
try {
ZipSecureFile.setMinInflateRatio(-1.0d);
is = new FileInputStream(pptxFile);
pptx = new XMLSlideShow(is);
// 指定index
int idx = 1;
Dimension pageSize = pptx.getPageSize();
// 缩放比例 3倍以下会迷糊
double image_rate = 4.0;
// 获取高度宽度
int imageWidth = (int) Math.floor(image_rate * pageSize.getWidth());
int imageHeight = (int) Math.floor(image_rate * pageSize.getHeight());
log.info("=>pptx解析完毕共:" + pptx.getSlides().size() + '张');
for (XSLFSlide xslfSlide : pptx.getSlides()) {
// 构造输出流和画布
BufferedImage img = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = img.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// 设置底色
graphics.setPaint(Color.white);
// 绘制区域与缩放比例
graphics.fill(new Rectangle2D.Float(0, 0, imageWidth, imageHeight));
graphics.scale(image_rate, image_rate);
//防止中文乱码 (这里可以为黑体或者宋体)--- 即使制定了字体也会出现部分字体或艺术字乱码的现象
for (XSLFShape shape : xslfSlide.getShapes()) {
if (shape instanceof XSLFTextShape) {
XSLFTextShape xslfTextShape = (XSLFTextShape) shape;
for (XSLFTextParagraph xslfTextParagraph : xslfTextShape) {
for (XSLFTextRun xslfTextRun : xslfTextParagraph) {
xslfTextRun.setFontFamily("黑体");
}
}
}
}
// 指定输出流
FileOutputStream out = null;
try {
xslfSlide.draw(graphics);
File pngFile = new File(saveFolder.getAbsolutePath() + String.format("/%d.png", idx++));
out = new FileOutputStream(pngFile);
// 输出图片
ImageIO.write(img, "png", out);
pngFileList.add(pngFile.getAbsolutePath());
log.info("=>第" + (idx - 1) + "张,存储路径:" + pngFile.getAbsolutePath());
} finally {
try {
if (out != null) {
out.flush();
out.close();
}
if (graphics != null) {
graphics.dispose();
}
if (img != null) {
img.flush();
}
} catch (IOException e) {
log.error("=>pptx转换流关闭异常", e);
}
}
}
} finally {
try {
if (is != null) {
is.close();
}
if (pptx != null) {
pptx.close();
}
} catch (Exception e) {
log.error("=>pptx转换流关闭异常", e);
}
}
log.info("=>pptx转换完成,共用时" + ((System.currentTimeMillis() - startTime) / 1000) + "秒");
return pngFileList;
}
public static void main(String[] args) throws Exception {
PPT2Pic.pptConver2Png(new File("C:\\Users\\Ming\\Desktop\\烟台家宽建维协同管理系统介绍材料.pptx"),
new File("C:\\Users\\Ming\\Desktop\\新建文件夹\\123"));
}
}
方案3:使用openoffice
优点: 免费、转换准确度搞
缺点:需要第三方软件支持、占用一定内存、转换时间较长,另外openoffice只能实现ppt转pdf,pdf转图片需要另外编写代码
OpenOffice.org 是一套跨平台的办公室软件套件,能在 Windows、Linux、MacOS X (X11)、和 Solaris 等操作系统上执行。它与各个主要的办公室软件套件兼容。OpenOffice.org 是自由软件,任何人都可以免费下载、使用、及推广它。
官方下载地址:http://www.openoffice.org/ (需要安装)
1. 引入jar包
注意: 这里有两种方案完成转换
1.是在程序运行前通过命令的方式启动openoffice
2.是在程序运行中通过代码启动,转换完毕后后关闭
二者基于的jar包不同 ,请仔细阅读maven依赖中后两个依赖的说明
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>jurt</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>ridl</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>juh</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.openoffice</groupId>
<artifactId>unoil</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 注意:jodconverter的2.2.2版本在中央仓库不具备,需要大家下载后手动安装到本地仓库
下载地址如下https://sourceforge.net/projects/jodconverter/files/JODConverter/
此外:2.2.1是不兼容docx和pptx的若需要兼容则采用2.2.2或者修改默认类配置
具体修改可参考:https://blog.csdn.net/zhangjunli/article/details/104941509
若采用jodconverter-core的方式则无关这个jar,也不需要关注兼容性问题
-->
<dependency>
<groupId>com.artofsolving</groupId>
<artifactId>jodconverter</artifactId>
<version>2.2.1</version>
</dependency>
<!-- 这个jar包是实现openOffice服务程序代码中启动的jar,与上边的jar包属于两种工作方式 --->
<dependency>
<groupId>com.github.livesense</groupId>
<artifactId>jodconverter-core</artifactId>
<version>1.0.5</version>
</dependency>
2.编写代码
1>方式1:命令式启动 + 代码转换
进入openoffice安装目录,通过cmd启动一个soffice服务,
启动的命令是
soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;"
/**
* 将ppt转换为pdf, 这个方法其实可用于docx ppt等转换为pdf 或者html
* @param sourceFile ppt文件
* @param pdfFile 要输出的pdf文件路径+名字
* @return pdf绝对路径
* @throws Exception 转换异常
*/
public static String officeToPDF(File sourceFile, File pdfFile) throws Exception {
// 获取后缀并校验
String suffix = sourceFile.getName().substring(sourceFile.getName().lastIndexOf(".") + 1);
if (!"ppt".equals(suffix) && !"pptx".equals(suffix)) {
log.error("文件格式异常,非ppt,pptx");
throw new RuntimeException("文件格式异常,非ppt,pptx");
}
if (!sourceFile.exists()) {
// 找不到源文件, 则返回false
throw new RuntimeException("ppt文件不存在");
}
// 如果目标路径不存在, 则新建该路径
if (!pdfFile.getParentFile().exists()) {
pdfFile.getParentFile().mkdirs();
}
// 获取openOffice服务的链接。默认为8100
OpenOfficeConnection connection = new SocketOpenOfficeConnection("127.0.0.1", 8100);
connection.connect();
//用于测试openOffice连接时间
log.info("链接成功,开始转换");
long start = System.currentTimeMillis();
DocumentConverter converter = new StreamOpenOfficeDocumentConverter(
connection);
converter.convert(sourceFile, pdfFile);
//测试word转PDF的转换时间
log.info("转换结束:" + ((System.currentTimeMillis() - start) / 1000));
connection.disconnect();
return pdfFile.getAbsolutePath();
}
// ========================================================
2> 方式2:程序中启动关闭openOffice服务
/**
* 打开服务器
* @return
*/
public static OfficeManager startService() {
OfficeManager officeManage = null;
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
try {
log.info("=>openOffice准备启动服务....");
configuration.setOfficeHome("C:\\Program Files (x86)\\OpenOffice 4");// 设置OpenOffice.org安装目录
configuration.setPortNumbers(8100); // 设置转换端口,默认为8100
configuration.setTaskExecutionTimeout(1000 * 60 * 5L);// 设置任务执行超时为5分钟
configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);// 设置任务队列超时为24小时
officeManage = configuration.buildOfficeManager();
officeManage.start(); // 启动服务
log.info("=>openOffice服务启动成功");
} catch (Exception ce) {
log.info("openOffice转换服务启动失败!详细信息:" + ce);
}
return officeManage;
}
// 关闭服务器
public static void stopService(OfficeManager officeManager) {
log.info("=>关闭office转换服务....");
if (officeManager != null) {
officeManager.stop();
}
log.info("=>关闭office转换成功!");
}
/**
* ppt 转pdf java程序启动方式
* @param sourceFile
* @param pdfFile
* @throws FileNotFoundException
*/
public static String convert2PDF(File sourceFile, File pdfFile) throws FileNotFoundException {
// 获取后缀并校验
String suffix = sourceFile.getName().substring(sourceFile.getName().lastIndexOf(".") + 1);
if (!"ppt".equals(suffix) && !"pptx".equals(suffix)) {
log.error("文件格式异常,非ppt,pptx");
throw new RuntimeException("文件格式异常,非ppt,pptx");
}
if (!sourceFile.exists()) {
// 找不到源文件, 则返回false
throw new RuntimeException("ppt文件不存在");
}
OfficeManager officeManager = startService();
if (officeManager ==null) {
throw new RuntimeException("openOffice服务启动失败");
}
log.info("=>进行文档转换转换:" + sourceFile.getAbsolutePath() + " --> " + pdfFile.getAbsolutePath());
long start = System.currentTimeMillis();
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
// 如果目标路径不存在, 则新建该路径
if (!pdfFile.getParentFile().exists()) {
pdfFile.getParentFile().mkdirs();
}
converter.convert(sourceFile,pdfFile);
log.info("转换成功:" + ((System.currentTimeMillis() - start) / 1000));
stopService(officeManager);
return pdfFile.getAbsolutePath();
}
测试发现jodconverter-core的方式通过wps打开的ppt无法完成转换,pptx可以转换,office打开的ppt和pptx都可以
这里顺便说一下pdf转图片,使用pdfbox
- 引入依赖
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.20</version>
</dependency>
- 实现代码转换
/**
* dpi越大转换后越清晰,相对转换速度越慢
*/
private static final Integer DPI = 100;
/**
* 转换后的图片类型
*/
private static final String IMG_TYPE = "png";
/**
* pdf转图片
*
* @param pdfFile pdf路径
* @param folderPath 图片保存位置
* @return
* @throws IOException
*/
public static List<String> pdfToImage(File pdfFile, File folderPath) throws IOException {
log.info("=>pdf转图片");
log.info("=>pdf路径为:" + pdfFile.getAbsolutePath());
log.info("=>要存储的图片路径为:" + folderPath.getAbsolutePath());
long start = System.currentTimeMillis();
// 返回值
List<String> result = new ArrayList<>();
log.info("=>正在读取pdf");
PDDocument document = PDDocument.load(new FileInputStream(pdfFile));
PDFRenderer renderer = new PDFRenderer(document);
FileOutputStream out = null;
log.info("=>读取完毕开始转换,共"+document.getNumberOfPages() +"张");
for (int i = 0; i < document.getNumberOfPages(); ++i) {
BufferedImage bufferedImage = renderer.renderImageWithDPI(i, DPI);
File pngFile = new File(folderPath.getAbsolutePath() + String.format("/%d.png", (i+1)));
out = new FileOutputStream(pngFile);
ImageIO.write(bufferedImage, IMG_TYPE, out);
log.info("=>第"+(i+1)+"张转换完毕,"+pngFile.getAbsolutePath());
result.add(pngFile.getAbsolutePath());
out.close();
}
log.info("=>转换结束-成功," + ((System.currentTimeMillis() - start) / 1000) + "秒");
return result;
}