前言
突然有一天在业务系统中发现OpenOffice转换word为pdf时,出现个别中文字丢失以及格式发生变化。这在业务系统中预览合同等重要附件是致命的。Google了半天也没找到问题所在。于是采用LibreOffice进行转换,看看转换效果。office文件在线预览原理一样,先转换成pdf,然后采用pdf. js预览。
问题复现
原word文件
OpenOffice转换后pdf文件
链接
- 关于OpenOffice的使用方法,参考《跨平台(uni-app)文件在线预览解决方案》第四章节
- 关于pdf.js的使用方法,参考《跨平台(uni-app)文件在线预览解决方案》
- LibreOffice和OpenOffice中文乱码问题,文后说明解决方法
快速开始
下载
下载最新版本,目前是7.0.1
下载三个包:
- 主包:LibreOffice_7.0.1_Linux_x86-64_rpm.tar.gz
- SDK:LibreOffice_7.0.1_Linux_x86-64_rpm_sdk.tar.gz
- 语言包:LibreOffice_7.0.1_Linux_x86-64_rpm_langpack_zh.tar.gz
安装
- 主包
cd LibreOffice_7.0.1.2_Linux_x86-64_rpm/RPMS
yum localinstall -y *.rpm
- SDK
cd LibreOffice_7.0.1.2_Linux_x86-64_rpm_sdk/RPMS
yum localinstall -y *.rpm
- 语言包
cd LibreOffice_7.0.1.2_Linux_x86-64_rpm_langpack_zh-CN
yum localinstall -y *.rpm
启动
/opt/libreoffice7.0/program/soffice --headless --accept="socket,host=127.0.0.1,port=8101;urp;" --nofirststartwizard > /opt/server/libre.log 2>&1 &
# 自启动
将以上命令添加到/etc/rc.local中
测试
libreoffice7.0 --headless --invisible --convert-to pdf 原office文件 --outdir 输出目录
Java集成
安装jar
org.jodconverter
jodconverter-local
4.3.0
配置文件libre.properties
在resources目录下新建libre.properties
# LibreOffice主目录
libreOfficeHome=/opt/libreoffice7.0
# 开启多个LibreOffice进程,每个端口对应一个进程
# portNumbers=2002,2003
portNumbers=8101
# 任务执行超时为5分钟
taskExecutionTimeoutMinutes=5
# 任务队列超时为1小时
taskQueueTimeoutHours=1
读取配置,并连接LibreOffice服务
新建类:OfficeManagerInstance.java
package cn.sccl.common.config;
import org.jodconverter.core.office.OfficeManager;
import org.jodconverter.local.office.LocalOfficeManager;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Properties;
@Component
public class OfficeManagerInstance {
private static OfficeManager INSTANCE = null;
public static synchronized void start() {
officeManagerStart();
}
@PostConstruct
private void init() {
try {
Properties properties = PropertiesLoaderUtils.loadAllProperties("libre.properties");
String[] portNumbers = properties.getProperty("portNumbers", "").split(",");
int[] ports = new int[portNumbers.length];
for (int i = 0; i < portNumbers.length; i++) {
ports[i] = Integer.parseInt(portNumbers[i]);
}
LocalOfficeManager.Builder builder = LocalOfficeManager.builder().install();
builder.officeHome(properties.getProperty("libreOfficeHome", ""));
builder.portNumbers(ports);
builder.taskExecutionTimeout(Long.parseLong(properties.getProperty("taskExecutionTimeoutMinutes", "")) * 1000 * 60); // minute
builder.taskQueueTimeout(Long.parseLong(properties.getProperty("taskQueueTimeoutHours", "")) * 1000 * 60 * 60); // hour
INSTANCE = builder.build();
officeManagerStart();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void officeManagerStart() {
if (INSTANCE.isRunning()) {
return;
}
try {
INSTANCE.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
工具类LibreOfficeUtil
新建工具类:LibreOfficeUtil.java
package cn.sccl.common.util;
import cn.sccl.common.config.OfficeManagerInstance;
import org.jodconverter.local.JodConverter;
import java.io.File;
public class LibreOfficeUtil {
/**
* 利用 JodConverter 将 Offfice 文档转换为 PDF(要依赖 LibreOffice),该转换为同步转换,返回时就已经转换完成
*/
public static boolean convertOffice2PDFSyncIsSuccess(File sourceFile, File targetFile) {
try {
OfficeManagerInstance.start();
JodConverter.convert(sourceFile).to(targetFile).execute();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 利用 LibreOffice 将 Office 文档转换成 PDF,该转换是异步的,返回时,转换可能还在进行中,转换是否有异常也未可知
* @param filePath 目标文件地址
* @param targetFilePath 输出文件夹
* @return 子线程执行完毕的返回值
*/
public static int convertOffice2PDFAsync(String filePath, String fileName, String targetFilePath) throws Exception {
String command;
int exitStatus;
String osName = System.getProperty("os.name");
String outDir = targetFilePath.length() > 0 ? " --outdir " + targetFilePath : "";
if (osName.contains("Windows")) {
command = "cmd /c cd /d " + filePath + " && start soffice --headless --invisible --convert-to pdf ./" + fileName + outDir;
} else {
command = "libreoffice6.3 --headless --invisible --convert-to pdf:writer_pdf_Export " + filePath + fileName + outDir;
}
exitStatus = executeOSCommand(command);
return exitStatus;
}
/**
* 调用操作系统的控制台,执行 command 指令
* 执行该方法时,并没有等到指令执行完毕才返回,而是执行之后立即返回,返回结果为 0,只能说明正确的调用了操作系统的控制台指令,但执行结果如何,是否有异常,在这里是不能体现的,所以,更好的姿势是用同步转换功能。
*/
private static int executeOSCommand(String command) throws Exception {
Process process;
process = Runtime.getRuntime().exec(command); // 转换需要时间,比如一个 3M 左右的文档大概需要 8 秒左右,但实际测试时,并不会等转换结束才执行下一行代码,而是把执行指令发送出去后就立即执行下一行代码了。
int exitStatus = process.waitFor();
if (exitStatus == 0) {
exitStatus = process.exitValue();
}
// 销毁子进程
process.destroy();
return exitStatus;
}
}
使用方法
boolean ret = LibreOfficeUtil.convertOffice2PDFSyncIsSuccess(sourceFile, pdfFile);
效果对比
效果明显比OpenOffice效果好,就转换速率来说,差不多时间,这个就没有详细计算了。
中文乱码问题解决
不管是用LibreOffice还是OpenOffice,都会遇到中文乱码问题。统一解决办法如下:
环境:CentOS7
- 查看字体目录
vim /etc/fonts/fonts.conf
,默认在/usr/share/fonts/ - 安装中文字体
mkdir /usr/share/fonts/chinese
cd /usr/share/fonts/chinese
# 将windows字体拷贝到该目录,如下:搜索“简体”,拷贝所有
chmod 755 *.TTF
chmod 755 *.TTC
# (如果提示 mkfontscale: command not found,需自行安装 # yum install mkfontscale)
mkfontscale
mkfontdir
# (如果提示 fc-cache: command not found,则需要安装# yum install fontconfig )
fc-cache -fv
# 安装完成,重启LibreOffice或OpenOffice服务
赞助作者,互相交流