当前位置: 首页 > news >正文

SpringBoot项目用Aspose-Words将Word转换为PDF文件正常显示中文的正确姿势

简介

目前需要实现将Word转换为PDF的功能,但在实现过程中遇到了一个问题:生成的PDF文件中的中文变成了方框。

Maven依赖

<dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>20.9</version><scope>compile</scope>
</dependency>
<dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.4.0</version>
</dependency>

源代码

起初,是希望使用aspose-words依赖将Word转换为PDF文件,但是如果Linux中未安装字体库(字体默认路径:/usr/share/fonts下无中文字体),中文字符无法正常显示,实际显示的是□□□,以下代码是最初的版本:

import cn.hutool.system.SystemUtil;
import com.aspose.words.Document;
import com.aspose.words.FontSettings;
import com.aspose.words.PdfSaveOptions;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Component;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class ExcelHandlerTest {/*** word转换为pdf** @param docx docx* @throws Exception*/public OutputStream wordToPdf(XWPFDocument docx) throws Exception {InputStream wordInput = null;OutputStream pdfOutput = new ByteArrayOutputStream();try {wordInput = convertToInputStream(docx);//通过aspose-words中的类转换文件Document wordDoc = new Document(wordInput);//判断当前是否为Linux系统String osName = SystemUtil.getOsInfo().getName();boolean isLinux = osName.toLowerCase().contains("linux");if (isLinux) {FontSettings settings = new FontSettings();//设置汉字字体,否则转换后的文档汉字显示异常。settings.setFontsFolder("/usr/share/fonts", false);wordDoc.setFontSettings(settings);}PdfSaveOptions pso = new PdfSaveOptions();wordDoc.save(pdfOutput, pso);} catch (Exception e) {throw new RuntimeException("导出PDF异常");} finally {if (wordInput != null) {wordInput.close();}if (pdfOutput != null) {pdfOutput.close();}}return pdfOutput;}public static InputStream convertToInputStream(XWPFDocument doc) throws Exception {ByteArrayOutputStream out = new ByteArrayOutputStream();// 写入文档到输出流doc.write(out);// 将输出流转换为字节数组byte[] docBytes = out.toByteArray();// 关闭输出流out.close();// 将字节数组转换为输入流return new ByteArrayInputStream(docBytes);}
}

用以上代码处理后,则出现了使用本地运行和线上运行结果不一致的问题,本地PDF能够正常显示,而线上导出的中文均显示为方框□□□。

经过排查,最后得出了结论:因为本地是通过windows系统启动,默认安装了一部分中文字体库,线上则是Linux系统,并没有安装对应的中文字体库,因此才导致线上中文显示有误的问题。

解决方案

  1. 直接在Linux中安装所需字体库(可安装在字体库默认地址下,也可在其他路径下),此方式无需修改代码。
  2. 字体库在项目下的resources文件夹下(.setFontsFolder方法需获取字体库的绝对路径,无法直接通过resources路径获取,但可先将resources下字体文件转成流,再创建临时文件进行处理),此方式需修改代码。

Aspose-Words源代码(非实际代码,仅介绍大概的实现逻辑)

public static String[] handler(String var0) {//这里是通过new File()方式读取字体库文件File[] var1 = (new File(var0)).listFiles();//具体实现逻辑……
}

修改后的代码

package com.baseus.finance.infrastructure.utils;import com.aspose.words.Document;
import com.aspose.words.FontSettings;
import com.aspose.words.SaveFormat;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Component;import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashSet;
import java.util.Set;@Component
public class ExcelHandlerTest {private static final Set<String> REQUIRED_FONTS = new HashSet<>();static {// 添加其他必要的字体文件REQUIRED_FONTS.add("msyhl.ttc");REQUIRED_FONTS.add("msyhbd.ttc");REQUIRED_FONTS.add("msyh.ttc");}private static final Path FONT_CACHE_DIR = Paths.get(System.getProperty("java.io.tmpdir"), "fontCache");/*** word转换为pdf* @param docx docx* @throws Exception*/public OutputStream wordToPdf(XWPFDocument docx) throws Exception {// 创建一个临时文件来保存XWPFDocumentFile tempFile = null;try {tempFile = File.createTempFile(String.valueOf(SnowFlakeUtil.nextId()), ".docx");try (FileOutputStream fos = new FileOutputStream(tempFile)) {// 将XWPFDocument写入临时文件docx.write(fos);}// 使用Aspose.Words加载临时文件Document wordDoc = new Document(tempFile.getPath());// 设置字体目录,确保Aspose.Words能够找到中文字体FontSettings fontSettings = new FontSettings();// 确保字体缓存目录存在if (!Files.exists(FONT_CACHE_DIR)) {Files.createDirectories(FONT_CACHE_DIR);}// 复制必要的字体文件到缓存目录copyResourceFontsToCacheDir();fontSettings.setFontsFolder(FONT_CACHE_DIR.toString(), true);wordDoc.setFontSettings(fontSettings);// 使用ByteArrayOutputStream来捕获PDF输出ByteArrayOutputStream pdfOutput = new ByteArrayOutputStream();// 将Aspose.Words的Document保存为PDFwordDoc.save(pdfOutput, SaveFormat.PDF);// 返回PDF内容的字节数组return pdfOutput;} finally {// 清理:删除临时文件if (tempFile != null && tempFile.exists()) {tempFile.delete();}}}private void copyResourceFontsToCacheDir() throws Exception {for (String fontFile : REQUIRED_FONTS) {Path fontPath = FONT_CACHE_DIR.resolve(fontFile);if (!Files.exists(fontPath)) {try (InputStream fontStream = getClass().getClassLoader().getResourceAsStream("fonts/" + fontFile)) {if (fontStream == null) {throw new IllegalArgumentException("Font file not found: " + fontFile);}Files.copy(fontStream, fontPath, StandardCopyOption.REPLACE_EXISTING);}}}}
}

总结

  1. 考虑到如果选择第一种方式会增加运维难度(部署不同的服务器都需要安装对应的中文字体库),最终方案选择了第二种。
  2. 在使用第二种解决方案之前,本来是考虑直接通过获取resources下的字体库的方式,但项目是jar包方式启动,获取到的字体库的地址并不是一个文件位置,因此才考虑使用文件流的方式进行处理。
  3. 对于碰到的问题,首先要确定导致问题发生的原因,应采取多次试错的方法,再考虑对应的解决方案,这样才能复用到下一次的问题解决当中。

http://www.mrgr.cn/news/20922.html

相关文章:

  • 在深度学习计算机视觉的语义分割中,Boundary和Edge的区别是?
  • 波导模式分析2 用于圆TE01模式高功率传输线的大型多模波导滤波器
  • 【新闻转载】2024年上半年勒索软件态势分析:团伙数量激增,攻击策略多样化
  • 大模型日报|9 篇必读的大模型论文
  • 二级菜单的两种思路(完成部分)
  • 行业域名有哪些?
  • Day17_0.1基础学习MATLAB学习小技巧总结(17)——字符向量元胞数组
  • MySQL之对数据库和表的操作
  • 元宇宙先驱,城市区块链
  • CSS学习6--背景图片、颜色、位置、附着、简写、透明、缩放、多背景、凹凸文字、导航栏例子
  • 什么是数据结构三要素?
  • 服务器测试之GPU基础汇总
  • [米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-32 ADC模块FEP-DAQ7606采集显示波形方案
  • 汽车智能驾驶算法汇总
  • 【区块链 + 人才服务】FISCO BCOS 高校实训和管理平台 | FISCO BCOS应用案例
  • 耗费2.5月!打造Word神器小羊助手:个人开发全栈项目
  • FFmpeg的安装教程
  • uniapp组件知识记录
  • 【零基础必看的数据库教程】——SQL SELECT DISTINCT 语句
  • 伴奏提取消除人声如何操作?轻松几步玩转音乐世界