# office-conver
**Repository Path**: 98sun/office-conver
## Basic Information
- **Project Name**: office-conver
- **Description**: 本项目是一个用于将Microsoft Office格式的文件(如Word文档、PDF预览文件、PowerPoint演示文稿)转换为图片格式的工具。该项目利用Apache POI库、pdfbox库解析和处理Office文件,并通过图像处理技术将每个页面或幻灯片转换为独立的图片文件。它提供了简单示例,使用户能够方便地将Office文件转换为图片,以便开发能快速上手使用。
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 3
- **Created**: 2024-11-19
- **Last Updated**: 2024-11-19
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
最近遇到了一个需求,需要在前端小程序中嵌入展示Office文件的功能。然而,前端使用开源组件进行在线预览会导致性能消耗较大的问题(转半天圈圈)。产品理想的效果是用户上传Office文件后,浏览起来与页面一样流畅。
>没错,作为服务端的老铁,可以提供更强大的计算资源和处理能力来支持前端小伙伴实现需求(We are a team🏠)!
这种情况下,可以在服务端使用开源插件对文件进行预览切片,将文件的预览效果保持为一张一张的图片,用户预览时直接加载图片即可。此方法带来的另一个好处是可以做懒加载和缓存功能,预览过的文件图片可以缓存,再次预览的时候可以快速加载,无需消耗流量!
**心急铁铁**,可直接拉下项目使用:[office-conver](https://gitee.com/hgw689/office-conver)
# PDF转图片
Apache PDFBox是一个功能丰富而强大的PDF处理库,提供了广泛的功能和工具来处理和操作PDF文档。它是一个开源项目,具有广泛的社区支持和活跃的开发。你可以在[Apache PDFBox的官方网站](https://pdfbox.apache.org/)上找到更多的文档、示例和API参考,以帮助你使用和了解该库的更多功能。
## 1. 万事第一步
```xml
org.apache.pdfbox
pdfbox
${pdfbox.version}
```
## 2. 撸代码
```java
/**
* Description: pdf转换为图片转换器
*
* @author YanAn
* @date 2023/9/20 14:11
*/
@Slf4j
public class PDFToPNGConverter extends BaseConverter {
private static final float IMAGE_SCALE = 8;
public PDFToPNGConverter(String inputPath) {
super(inputPath);
}
@Override
public List convertToPNG() {
InputStream is = null;
try {
is = getInputStream(inputFileUrl);
PDDocument document = PDDocument.load(is);
PDFRenderer renderer = new PDFRenderer(document);
int pageSize = document.getNumberOfPages();
for (int i = 0; i < pageSize; i++) {
BufferedImage img = renderer.renderImage(i, IMAGE_SCALE);
// save the output
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
javax.imageio.ImageIO.write(img, "png", bos);
String url = uploadFileToOss(bos);
outPathUrlList.add(url);
}
}
} catch (Exception e) {
log.error("pdf转换图片失败,{}", e.getMessage());
throw new RuntimeException("pdf转换图片失败" + e.getMessage());
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return outPathUrlList;
}
}
```
# PPT/PPTX转图片
[Apache POI](https://poi.apache.org/)(Poor Obfuscation Implementation)是一个开源的Java库,用于处理和操作Microsoft Office格式的文件,包括Word文档(.doc和.docx)、Excel电子表格(.xls和.xlsx)、PowerPoint演示文稿(.ppt和.pptx)等。它提供了丰富的API和功能,使开发人员能够读取、创建和修改Office文件。小编的另外一篇基于poi实现ppt的骚操作博文[如何使用POI读取模板PPT填充数据并拼接至目标文件](https://blog.csdn.net/m0_49183244/article/details/130109694)
## 1. 万事第一步
```xml
org.apache.poi
poi
4.1.2
org.apache.poi
poi-ooxml
4.1.2
org.apache.poi
poi-scratchpad
4.1.2
```
## 2. 撸代码
>抽象ppt转换为图片转换器
```java
/**
* Description: 抽象ppt转换为图片转换器
*
* @author YanAn
* @date 2023/9/20 14:11
*/
public abstract class AbstractPPTToPNGConverter extends BaseConverter {
private final static double IMAGE_SCALE = 8;
public AbstractPPTToPNGConverter(String inputPath) {
super(inputPath);
}
/**
* 幻灯片转换图片方法并且上传oss
*
* @param pgWidth 宽
* @param pgHeight 高
* @param slide 幻灯片
* @return 图片于oss文件链接
* @throws IOException
*/
protected String toPNG(int pgWidth, int pgHeight, Slide slide) throws IOException {
int imageWidth = (int) Math.floor(IMAGE_SCALE * pgWidth);
int imageHeight = (int) Math.floor(IMAGE_SCALE * pgHeight);
BufferedImage img = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = img.createGraphics();
graphics.setPaint(Color.white);
graphics.fill(new Rectangle2D.Float(0, 0, pgWidth, pgHeight));
graphics.scale(IMAGE_SCALE, IMAGE_SCALE);
slide.draw(graphics);
// save the output
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
bos = new ByteArrayOutputStream();
javax.imageio.ImageIO.write(img, "png", bos);
return uploadFileToOss(bos);
} finally {
bos.close();
}
}
}
```
> ppt转换为图片转换器
```java
/**
* Description: ppt转换为图片转换器
*
* @author YanAn
* @date 2023/9/21 13:35
*/
@Slf4j
public class PPTToPNGConverter extends AbstractPPTToPNGConverter{
public PPTToPNGConverter(String inputPath) {
super(inputPath);
}
@Override
public List convertToPNG() {
InputStream is = null;
HSLFSlideShow ppt = null;
try {
is = getInputStream(inputFileUrl);
ppt =new HSLFSlideShow(is);
Dimension pgSize = ppt.getPageSize();
for (HSLFSlide slide : ppt.getSlides()) {
String url = toPNG(pgSize.width, pgSize.height, slide);
outPathUrlList.add(url);
}
} catch (IOException e) {
log.error("ppt转换图片失败,{}", e.getMessage());
throw new RuntimeException("ppt转换图片失败" + e.getMessage());
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ppt != null) {
ppt.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return outPathUrlList;
}
}
```
>pptx转换为图片转换器
```java
/**
* Description: pptx转换为图片转换器
*
* @author YanAn
* @date 2023/9/21 13:35
*/
@Slf4j
public class PPTXToPNGConverter extends AbstractPPTToPNGConverter {
public PPTXToPNGConverter(String inputPath) {
super(inputPath);
}
@Override
public List convertToPNG() {
InputStream is = null;
XMLSlideShow ppt = null;
try {
is = getInputStream(inputFileUrl);
ppt = new XMLSlideShow(is);
Dimension pgSize = ppt.getPageSize();
for (XSLFSlide slide : ppt.getSlides()) {
String url = toPNG(pgSize.width, pgSize.height, slide);
outPathUrlList.add(url);
}
} catch (IOException e) {
log.error("pptx转换图片失败,{}", e.getMessage());
throw new RuntimeException("pptx转换图片失败" + e.getMessage());
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ppt != null) {
ppt.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return outPathUrlList;
}
}
```
## 验收一下

# HTML转图片
## 1. 万事第一步
```xml
org.xhtmlrenderer
core-renderer
R8
net.sourceforge.nekohtml
nekohtml
1.9.14
gui.ava
html2image
2.0.1
```
这里 gui.ava 的jar包拉不下。本`src/main/resources/file`提供了相关jar包,同学们可将jar包直接copy至企业私仓或本地仓库即可。

## 2. 撸代码
> HTML转换器
```java
package com.hgw.officeconver.converter;
import gui.ava.html.parser.HtmlParserImpl;
import gui.ava.html.renderer.ImageRenderer;
import gui.ava.html.renderer.ImageRendererImpl;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
/**
* Description: HTML转换器
*
* @author LinHuiBa-YanAn
* @date 2023/10/19 15:51
*/
@Slf4j
public class HtmlToPNGConverter extends BaseConverter{
public HtmlToPNGConverter(String inputSource) {
super(inputSource);
}
@Override
public List convertToPNG() {
if (Objects.isNull(inputSource)) {
throw new RuntimeException( "html内容不能为空");
}
HtmlParserImpl htmlParser = new HtmlParserImpl();
htmlParser.loadHtml(inputSource);
ImageRenderer imageRenderer = new ImageRendererImpl(htmlParser);
BufferedImage img = imageRenderer.getBufferedImage();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ImageIO.write(img, "png", bos);
} catch (IOException e) {
log.error("html转换图片失败,{}", e);
throw new RuntimeException("html转换图片失败" + e.getMessage());
}
outPathUrlList.add(uploadFileToOss(bos));
return outPathUrlList;
}
}
```
## 验收一下
```java
@Test
public void htmlToPNGConverterTest() {
String html = "测试内容编辑
阿萨德和i
地址:杭州市余杭区溪西八方城11幢
来个图片
图片右侧文案
分割线
内容缩紧速度能达到收到
- 阿斯顿
- d s d
- 说到底
- 说到底
- 3434
- 当时的
背景色

\uFEFF
无语佛啊啊\uFEFF
sad

";
HtmlToPNGConverter htmlToPNGConverter = new HtmlToPNGConverter(html);
List urlList = htmlToPNGConverter.convertToPNG();
System.out.println(JSONObject.toJSONString(urlList));
}
```

效果如下:
~~~java
["https://message-center-gc.oss-cn-hangzhou.aliyuncs.com/20231020000000000c76f731b-a6bc-44e3-9e23-82eae3608b13.png"]
~~~
# 踩坑经历
> 自此office转换图片的功能基本实现,我们部署至服务器!
## 1、PPT/PPTX转换时中文乱码问题
当我们在本地测试一切ok,提测后部署到服务器之后突然收到了一个**BUG**(中文乱码,成方框`口`)!

那是因为ppt内容中字体不支持,服务器未安装中文字体,一般我们的服务器部署方案不支持做这件事(将所有的字体下载至镜像),业务代码的服务器都在 k8s 集群上,相对都是无状态的,没有什么其它额外的东西,如果将所有字体放进去整个镜像会特别大不太适合走 k8s这套了。若非要这样的话,推荐重新部署一台服务器,专门用于文件转换。当然,有条件的铁铁就不用考虑啦,直接下载所有字体!
> 如果未安装,可通过 yum -y install fontconfig 安装,然后在/usr/share 目录下会发现 fonts目录,下载中文字体如:heiti.ttf
>
> 拷贝到fonts目录下,chmod 赋权限。再次执行 fc-list :" class="has" data-src="/image/https://img-blog.csdnimg.cn/20181106180741352.png" height="54" src="/assets/images/photo.gif" width="738"/>
>
> 如果是docker环境,则可将上述安装步骤写入到dockerfile中。
**这里演示没有条件的解决方案**,下载 "苹方"字体,并在PPT/PPT转换文件时统一字体为 "苹方"(当然可以是其他字体,如“宋体”……)
第一步、下载字体

第二步、编写dockerfile安装字体
```
#PDF 转图片中文乱码#
RUN set -xe \
&& apk --no-cache add fontconfig
#&& apk --no-cache add ttf-dejavu fontconfig
COPY pingfang.ttf /usr/share/fonts/ttf-dejavu/pingfang.ttf
#PDF 转图片中文乱码#
```
第三步、转换器中指定字体

## 2、OOM问题
由于转换是ppt的每一页进行单独转换,如果ppt页数多,可能会慢。 解决办法,一种是减小上文中的image_rate,如设置为1。还有就是可以通过多线程并发转换,但是由于该转换操作是CPU密集型操作,所以需要根据机器性能决定。具体代码如下:
**文件转换器专用线程工具类**:
> 根据机器性能线程池配置如下(大家根据自己的服务器自行调**前三项**):
>
> + 核心线程池大小:5
> + 最大线程池大小:5
> + 阻塞工作队列:2
> + 拒绝策略:调用方执行(此处核心,请勿改动!当然有条件的除外)
>
> 因为本文主要是讲解office文件转换至图片,关于线程池的相关知识这里不做解释,给大家推荐一本书《Java并发编程的艺术》
```java
package com.hgw.officeconver.thread;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* Description: 文件转换器专用线程工具
*
* @author LinHuiBa-YanAn
* @date 2023/10/10 16:23
*/
@Slf4j
public class BizThreadPool {
private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.CallerRunsPolicy());
/**
* 线程执行(有返回值)
*
* @param supplier
* @param
* @return
*/
public static CompletableFuture supplyAsync(Supplier supplier) {
return CompletableFuture.supplyAsync(() -> {
try {
return supplier.get();
} catch (Exception e) {
log.warn("异步执行错误", e);
throw e;
}
}, threadPoolExecutor);
}
}
```
以PDF转换代码为例:

## 3、Cannot read JPEG2000 image: Java Advanced Imaging (JAI) Image I/O Tools are not installed 问题解决
我们发现pdf中包含JPEG2000格式的图片时,图片将渲染不出来。
pdf效果

转换后:

通过查看日志发现以下报错。

原因是不能读取JPEG2000格式的图片,需要引入以下工具。
~~~xml
com.github.jai-imageio
jai-imageio-core
1.4.0
com.github.jai-imageio
jai-imageio-jpeg2000
1.3.0
org.apache.pdfbox
jbig2-imageio
3.0.3
~~~
## 4、Could not read embedded TTF for font AAAAAI+MicrosoftYaHeiLight 问题处理
我们发现pdf中包含一些特殊字体时,解析之后将会是一串编码。
pdf效果:

转换后:

通过查看日志发现以下报错。

引入以下包:
```xml
com.itextpdf
itextpdf
5.5.10
```
## 5、html转换时中文乱码问题
老样子,我们在本地并没有出现,部署到linux服务器上之后便出现了中文乱码,如下图。

没毛病又是字体的问题,参考以下两位大佬:
+ [https://www.cnblogs.com/tlll/p/7853106.html](https://www.cnblogs.com/tlll/p/7853106.html)
+ [https://blog.csdn.net/zhaikaiyun/article/details/123837429](https://blog.csdn.net/zhaikaiyun/article/details/123837429)
解决方案,下载 sumSun.ttf 字体,配置至环境
```
RUN tar -xf /tmp/server-jre-8u202-linux-x64.tar.gz -C /opt && \
mkdir /opt/jdk1.8.0_202/jre/lib/fonts/fallback && \
cp /tmp/simsun.ttc /opt/jdk1.8.0_202/jre/lib/fonts/fallback/simsun.ttc && \
cp /tmp/simsun.ttf /opt/jdk1.8.0_202/jre/lib/fonts/fallback/simsun.ttf && \
rm -rf /tmp/* /var/cache/apk/*
ENV JAVA_HOME=/opt/jdk1.8.0_202 \
CLASSPATH=/opt/jdk1.8.0_202/lib \
PATH=${PATH}:/opt/jdk1.8.0_202/bin
```