最近做的需求需要实现在线预览ppt的功能,网上查了一遍,比较完美的方案都需要依赖第三方的服务或调用微软的在线接口,由于项目部署内网,同时为了不增加项目的复杂度,最终决定使用纯java实现,依赖 poi 将ppt 转成 png图片,然后再利用 itextpdf 将图片转成 pdf ,最后通过js实现预览。转换后的pdf效果图如下:
目前存在一个确定是 ppt字体全部统一换成了 宋体 ,以解决中文有些情况下乱码的问题,本来想研究下能不更换字体的方式,但是没时间,暂时就这样了。有解决方案的同学还请赐教。
下面直接上代码:
maven 依赖大致如下:
<!-- excel工具 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>ooxml-schemas</artifactId><version>1.4</version></dependency><dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>xdocreport</artifactId><version>2.0.2</version></dependency><!--pdf start--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.1</version></dependency><dependency><groupId>org.apache.pdfbox</groupId><artifactId>fontbox</artifactId><version>2.0.18</version></dependency><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.18</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency><dependency><groupId>org.apache.xmlgraphics</groupId><artifactId>batik-bridge</artifactId><version>1.12</version></dependency><!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator --><dependency><groupId>net.coobird</groupId><artifactId>thumbnailator</artifactId><version>0.4.8</version></dependency><!--end-->
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<byte[]> 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<byte[]> 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 areagraphics.setPaint(Color.white);graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));// renderppt.getSlides().get(i).draw(graphics);// save the outputByteArrayOutputStream 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 areagraphics.setPaint(Color.white);graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));// renderppt.getSlides().get(i).draw(graphics);// save the outputString 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<byte[]> 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<byte[]> 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 areagraphics.setPaint(Color.white);graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));// renderppt.getSlides().get(i).draw(graphics);// save the outputByteArrayOutputStream 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 areagraphics.setPaint(Color.white);graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));// renderppt.getSlides().get(i).draw(graphics);// save the outputString 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<byte[]> images) throws Exception {// Document doc = new Document();
// Document doc = new Document(PageSize.A4, 0, 0, 36.0F, 36.0F);//普通a4com.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);//写入PDFlog.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<byte[]> 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);}// 输出pdfbyte[] 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