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.mzph.cn/news/879329.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

海外云手机是否适合运营TikTok?

随着科技的迅猛发展&#xff0c;海外云手机逐渐成为改变工作模式的重要工具。这种基于云端技术的虚拟手机&#xff0c;不仅提供了更加便捷、安全的使用体验&#xff0c;还在电商引流和海外社媒管理等领域展示了其巨大潜力。那么&#xff0c;海外云手机究竟能否有效用于运营TikT…

mosh java 2.4 inheritance继承

1.面向对象编程的第三个特点 继承 继承的好处 java不能继承多个父级 代码文件结构 1.main.java package org.example; //package org.codewithme;//import org.example.UIControl;//TIP To <b>Run</b> code, press <shortcut actionId"Run"/> or /…

3C电子胶黏剂在手机制造方面有哪些关键的应用

3C电子胶黏剂在手机制造方面有哪些关键的应用 3C电子胶黏剂在手机制造中扮演着至关重要的角色&#xff0c;其应用广泛且细致&#xff0c;覆盖了手机内部组件的多个层面&#xff0c;确保了设备的可靠性和性能。以下是电子胶在手机制造中的关键应用&#xff1a; 手机主板用胶&…

率先搭载存内计算AI芯片,维迈通引领骑行通讯降噪革新

近日&#xff0c;高端骑行头盔耳机领导品牌维迈通&#xff08;VIMOTO&#xff09;三款新品XR、V10S、V10X全平台正式发售&#xff0c;创新搭载了知存科技&#xff08;Witmem&#xff09;高能效存内计算AI芯片&#xff0c;为骑行爱好者带来更极致的AI降噪体验。 作为一家专注摩托…

状压DP

状压DP 对于数据范围n<20的可以考虑状压DP 1.蒙德里安的梦想 题目描述 求把 N M NM NM 的棋盘分割成若干个 12 的的长方形&#xff0c;有多少种方案。 例如当$ N2&#xff0c;M4$ 时&#xff0c;共有 5 种方案。当 N 2 &#xff0c; M 3 N2&#xff0c;M3 N2&…

windows 创建新用户,并分配到指定组

右击电脑 -> 点击管理 在右边右击&#xff0c;选择新用户&#xff0c;输入相关信息创建 创建用户后&#xff0c;选择用户&#xff0c;右击&#xff0c;选择属性&#xff0c;添加 点击高级 点击立即查找&#xff0c;可以搜索出所有可用的组&#xff0c;为其选择即可

Java XML

1、XML文件介绍 配置文件&#xff1a;用来保存设置的一些东西。 拿IDEA来举例&#xff0c;比如设置的背景图片&#xff0c;字体信息&#xff0c;字号信息和主题信息等等。 &#xff08;1&#xff09;以前是用txt保存的&#xff0c;没有任何优点&#xff0c;而且不利于阅读&a…

停车位检测-停车场车位识别

YOLO Parking Spot 概述 停车场获取的图像训练了四个YOLO模型来检测车辆。目标是收集信息&#xff0c;并可能开发一种停车解决方案以改善交通流量并优化空间利用率。通过识别汽车&#xff0c;我们生成了一份报告&#xff0c;其中包含图像细节&#xff0c;如可用停车位的数量、…

官宣:Zilliz 在亚马逊云科技中国区正式开服!

01 Zilliz Cloud 正式上线亚马逊云科技宁夏区服务 9 月 4 日&#xff0c;Zilliz 正式官宣&#xff0c; Zilliz Cloud 正式上线亚马逊云科技在宁夏区的云服务。至此&#xff0c;Zilliz Cloud 已实现全球 5 大云 19 个节点 的全覆盖&#xff0c;成为全球首个提供海内外多云服务的…

《机器学习》—— SVD奇异值分解方法对图像进行压缩

文章目录 一、SVD奇异值分解简单介绍二、代码实现—SVD奇异值分解方法对图像进行压缩 一、SVD奇异值分解简单介绍 SVD&#xff08;奇异值分解&#xff09;是一种在信号处理、统计学、线性代数、机器学习等多个领域广泛应用的矩阵分解方法。它将任何 mn 矩阵 A 分解为三个特定矩…

从0书写一个softmax分类 李沐pytorch实战

输出维度 在softmax 分类中 我们输出与类别一样多。 数据集有10个类别&#xff0c;所以网络输出维度为10。 初始化权重和偏置 torch.norma 生成一个均值为 0&#xff0c;标准差为0.01,一个形状为size(num_inputs, num_outputs)的张量偏置生成一个num_outputs 10 的一维张量&a…

Kubernetes从零到精通(10-服务Service)

Service简介 Deployment这种工作负载能管理我们应用Pod的副本数&#xff0c;并实现动态的创建和销毁&#xff0c;所以Pod本身是临时资源&#xff08;IP随时可能变化&#xff09;。现在如果某组Pod A需要访问另一组Pod B&#xff0c;A就需要在应用的配置参数里动态跟踪并更改B的…

【数学建模】相关系数

第一部分&#xff1a;相关系数简介 总体与样本&#xff1a; 总体&#xff1a;指研究对象的全体&#xff0c;比如全国人口普查数据。样本&#xff1a;从总体中抽取的一部分个体&#xff0c;如通过问卷调查收集的学生数据。 皮尔逊相关系数&#xff1a; 总体皮尔逊相关系数&…

Linux 8250串口控制器

1 8250串口类型的识别 Intel HW都使用DesignWare 8250&#xff1a; drivers/mfd/intel-lpss-pci.c drivers/tty/serial/8250/8250_dw.c IIR寄存器的高2位bit7、bit6用来识别8250串口的类型&#xff1a; 0 - 8250&#xff0c;无FIFO 0 - 并且存在SCR&#xff08;Scratch registe…

安科瑞Acrel-1000DP分布式光伏监控系统平台的设计与应用-安科瑞 蒋静

针对用户新能源接入后存在安全隐患、缺少有效监控、发电效率无法保证、收益计算困难、运行维护效率低等通点&#xff0c;提出的Acrel-1000DP分布式光伏监控系统平台&#xff0c;对整个用户电站全面监控&#xff0c;为用户实现降低能源使用成本、减轻变压器负载、余电上网&#…

如何构建大数据治理平台,助力企业数据决策

建设背景 &#xff08;1&#xff09;什么是数据资产 资产由企业及组织拥有和控制&#xff0c;能够提供增值服务、带来经济利益的重要资源。 资产不但需要管理&#xff0c; 更需要运营。 &#xff08;2&#xff09;数据资产运营中的问题 数据资产运营中存在的问题主要包括以下…

CANopen协议的理解

本文的重点是对CANopen协议的理解&#xff0c;不是编程实现 参考链接 canopen快速入门 1cia301协议介绍_哔哩哔哩_bilibili CANopen是什么&#xff1f; CANopen通讯基础&#xff08;上&#xff09;_哔哩哔哩_bilibili CANopen概述 图1. CAN报文标准帧的格式 CAN的报文可简单…

docker-compose 部署 flink

下载 flink 镜像 [rootlocalhost ~]# docker pull flink Using default tag: latest latest: Pulling from library/flink 762bedf4b1b7: Pull complete 95f9bd9906fa: Pull complete a880dee0d8e9: Pull complete 8c5deab9cbd6: Pull complete 56c142282fae: Pull comple…

Redis搭建集群

功能概述 Redis Cluster是Redis的自带的官方分布式解决方案&#xff0c;提供数据分片、高可用功能&#xff0c;在3.0版本正式推出。 使用Redis Cluster能解决负载均衡的问题&#xff0c;内部采用哈希分片规则&#xff1a; 基础架构图如下所示&#xff1a; 图中最大的虚线部分…

路由器WAN口和LAN口有什么不一样?

“ 路由器WAN口和LAN口的区别&#xff0c;WAN是广域网端口&#xff0c;LAN是本地网端口。WAN主要用于连接外部网络&#xff0c;而LAN用来连接家庭内部网络&#xff0c;两者主要会在标识上面有区别。以往大部分路由器的WAN只有一个&#xff0c;LAN口则有四个或以上&#xff0c;近…