失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > PPT预览 转换为图片实现方案 Apache POI 实现时踩坑:含嵌入文件ppt转换报

PPT预览 转换为图片实现方案 Apache POI 实现时踩坑:含嵌入文件ppt转换报

时间:2023-07-19 06:59:01

相关推荐

PPT预览 转换为图片实现方案  Apache POI 实现时踩坑:含嵌入文件ppt转换报

前言

背景:最近项目需要实现PPT预览功能,以及项目APP上浏览ppt,初步方案是ppt转为图片。

实现

1、ppt转为pdf,然后pdf转为图片

该种实现,先将ppt转为pdf文件,实现方式有很多,可参考之前文章文档在线预览,将文档转为pdf

然后实现将pdf转为图片,实现方式有很多,这里介绍其中一种, apache pdfbox ,具体如下:

maven依赖:

<dependency><groupId>org.apache.pdfbox</groupId><artifactId>fontbox</artifactId><version>2.0.12</version></dependency><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.12</version></dependency>

java代码:

try {int dpi = 296;PDDocument pdDocument = PDDocument.load(new File("pdf file path"));PDFRenderer renderer = new PDFRenderer(pdDocument);int pageCount = pdDocument.getNumberOfPages();/* dpi越大转换后越清晰,相对转换速度越慢 */for (int i = 0; i < pageCount; i++) {File dstFile = new File("png file path");BufferedImage image = renderer.renderImageWithDPI(i, dpi);ImageIO.write(image, "png", dstFile);}}catch (Exception e){e.printStackTrace();}

转换之后,可自行添加finally对资源进行处理

2、利用apache poi直接将ppt转为图片

上述方案中,有两步实现,先转pdf,再转图片。转pdf方案中,比较靠谱快速的方法可能需要依赖外部 liberoffice 或者 openoffice. 实现ppt预览,也可通过apache poi 将ppt直接转为图片

apache poi提供丰富的office操作api,可直接通过java代码读写操作excel sheet,表格,内容等。也可以直接操作ppt slider,插入,读取内容等。

talk is cheap, show my code

maven 依赖:

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>${appache.poi.version}</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>${appache.poi.version}</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>${appache.poi.version}</version></dependency>

官方最新版本已经到了 4.0.0

由于 powrpoint 有ppt和pptx(office 之后),ppt文件本质是二进制文件,而pptx 是xml 。所以需要不同的处理。具体实现如下:

ppt,使用HSLFSlide 读取

FileInputStream is = new FileInputStream("ppt file path");HSLFSlideShow ppt = new HSLFSlideShow(is);Dimension pageSize = ppt.getPageSize();int idx = 1;try {for (HSLFSlide slide : ppt.getSlides()) {toPNG(pageSize.getWidth(), pageSize.getHeight(), slide);idx++;}} catch (Exception e) {throw e;} finally {if (is != null) {is.close();}if (ppt != null){ppt.close();}}

pptx 使用XSLF

FileInputStream is = new FileInputStream("pptx file path");XMLSlideShow pptx = new XMLSlideShow(is);Dimension pageSize = pptx.getPageSize();int idx = 1;try {for (XSLFSlide xslfSlide : pptx.getSlides()) {this.toPNG(pageSize.width, pageSize.height, xslfSlide );idx++;}} catch (Exception e) {throw e;} finally {if (is != null){is.close();}if (pptx != null){pptx.close();}}

topng 实现:

//image_rate 图片尺寸倍率,rate越大,图片越清晰,转换时间越长int imageWidth = (int) Math.floor(image_rate * pageSize.width);int imageHeight = (int) Math.floor(image_rate * pageSize.height);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);// clear the drawing areagraphics.setPaint(Color.white);graphics.fill(new Rectangle2D.Float(0, 0, imageWidth, imageHeight));graphics.scale(this.IMAGE_RATE, this.IMAGE_RATE);FileOutputStream out = null;try {xslfSlide.draw(graphics);out = new FileOutputStream(new File("png file path"));javax.imageio.ImageIO.write(img, "png", out);} catch (IOException ioe) {ioe.printStackTrace();} catch (Exception e) {e.printStackTrace();} finally {try {if (out != null) {out.flush();out.close();}} catch (IOException ioe) {ioe.printStackTrace();}if (graphics != null){graphics.dispose();}if (img != null){img.flush();}}

这样能初步实现ppt转图片,实际过程中有如下几个问题:

1、中文 问题: 如果在本地测试觉得一切ok,那么部署到测试环境或者线上就可能出现中文乱码问题。

原因可能是ppt内容中字体不支持,服务器未安装中文字体。 如linux上,可 通过fc-list :lang=zh 查看。

如果未安装,可通过yum -y install fontconfig 安装,然后在/usr/share 目录下会发现 fonts目录,下载中文字体如:heiti.ttf

拷贝到fonts目录下,chmod 赋权限。再次执行 fc-list :lang=zh 命令,显示如下,则表示安装成功,也可以安装宋体等其他字体

如果是docker环境,则可将上述安装步骤写入到dockerfile中。

安装完成字体后,代码中设置字体如下:

ppt:

for (HSLFShape shape : slide.getShapes()) {if (shape instanceof HSLFTextShape) {HSLFTextShape sh = (HSLFTextShape) shape;List<HSLFTextParagraph> textParagraphs = sh.getTextParagraphs();for (HSLFTextParagraph hslfTextParagraph : textParagraphs) {List<HSLFTextRun> textRuns = hslfTextParagraph.getTextRuns();for (HSLFTextRun hslfTextRun : textRuns) {hslfTextRun.setFontFamily("宋体");hslfTextRun.setFontIndex(1);}}}}

pptx:

for (XSLFShape shape : slide.getShapes()){if (shape instanceof XSLFTextShape){XSLFTextShape sh = (XSLFTextShape) shape;List<XSLFTextParagraph> textParagraphs = sh.getTextParagraphs();for (XSLFTextParagraph xslfTextParagraph : textParagraphs) {List<XSLFTextRun> textRuns = xslfTextParagraph.getTextRuns();for (XSLFTextRun xslfTextRun : textRuns) {xslfTextRun.setFontFamily("宋体");}}}}

经上述操作,中文问题基本能解决了

2、如果pptx文件中含有嵌入文件,即在ppt中插入外部文件,可能会报错:

java.lang.ArrayStoreExceptionat java.lang.System.arraycopy(Native Method)at org.apache.xmlbeans.impl.values.XmlObjectBase._typedArray(XmlObjectBase.java:409)at org.apache.xmlbeans.impl.values.XmlObjectBase.selectPath(XmlObjectBase.java:457)at org.apache.xmlbeans.impl.values.XmlObjectBase.selectPath(XmlObjectBase.java:415)at org.apache.poi.xslf.usermodel.XSLFDrawing.<init>(XSLFDrawing.java:44)at org.apache.poi.xslf.usermodel.XSLFSheet.initDrawingAndShapes(XSLFSheet.java:201)at org.apache.poi.xslf.usermodel.XSLFSheet.getShapes(XSLFSheet.java:188)at org.apache.poi.sl.draw.DrawSheet.draw(DrawSheet.java:55)at org.apache.poi.sl.draw.DrawSlide.draw(DrawSlide.java:41)at org.apache.poi.xslf.usermodel.XSLFSlide.draw(XSLFSlide.java:307)

ppt文件不会有该问题,主要是上文中提到,pptx 可解析为xml文件,解析为XmlObject 时,类型转换错误,

XmlObjectBase中:

System.arraycopy(input, 0, result, 0, input.length);

debug源码发现,input 数组,三个元素类型不同,如下:

至于完整的pptx文件解析后的xml文件,有兴趣的同学可以自己看看,这里就不贴出来了。

百思不得其姐,一度想 通过 poi remove掉这个文件,发现事情没那么简单,然后准备祈祷不要有嵌入文件的pptx时,发现有一老哥和我一样的问题,参考:可能需要翻墙

原来是其他pom引入中含有xmlbeans 2.3 版本。

解决办法是确保xmlbeans 版本到2.6 。希望有老哥遇到该问题时能解决,找到依赖源, exclusion 一下。

3、转换慢,OOM等问题

由于转换是ppt的每一页进行单独转换,如果ppt页数多,可能会慢。 解决办法,一种是减小上文中的image_rate,如设置为1.

还有就是可以通过多线程并发转换,但是由于该转换操作是CPU密集型操作,所以需要根据机器性能决定。具体代码如下:

for (XSLFSlide slide : ppt.getSlides()) {CompletableFuture<byte[]> future = CompletableFuture.supplyAsync(() -> toPNG(pgsize.width, pgsize.height, slide, targetType), workers).handle((targetFilePath, e) -> {if (e != null) {logger.error("covertPPTX error", e);return null;} else {return targetFilePath;}});resultMap.put(idx, future);idx++;}

方式有很多种

并发转换时,一定要注意资源的释放,否则会容易出现OOM。

后记

本文主要是开发过程中问题记录,比较繁杂,参考了官网及一些博客,如有侵权,立马删除。

PPT在线预览 转换为图片实现方案 Apache POI 实现时踩坑:含嵌入文件ppt转换报错 ArrayStoreException

如果觉得《PPT预览 转换为图片实现方案 Apache POI 实现时踩坑:含嵌入文件ppt转换报》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。