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

java实现ofd转pdf

提示:JAVA实现ofd转pdf、ofd转pdf时,中文识别失败的解决方案、ofd转pdf时,字体加粗的解决方案、本地ofd转换pdf正常,服务器中ofd转pdf中文失效的解决方案、代码里有字体文件,但是程序读取不到的解决方案、pdf转换时中文乱码问题解决

文章目录

  • 一、代码实现
    • 1、pom依赖
    • 2、代码
    • 3、效果
  • 二、相关问题
    • 1.转换后的字体加粗
      • 1.1、问题现象图
    • 2.中文识别失败
      • 2.1、问题现象图
  • 三、字体问题解决方案
    • 3.1、服务器加上相关字体
    • 3.2、代码读取字体
      • 3.2.1、ofd转换pdf代码
      • 3.2.2、加载字体代码
  • 总结


简单说一下背景吧。我负责的机票业务线,要求开具电子行程单。但是由于供应商的不同,给提供的行程单也不同。有的能同时提供ofd和pdf(比如ibe+渠道),有的只提供ofd(比如易宝渠道),但是结算人员给客户提供的时候,需要同时提供ofd和pdf,这就需要研发人员手动生成pdf。

当然,xml、ofd是有电子签名的,pdf是没有电子签名的,我们也没法通过没电子签名的去生成有电子签名的文件,也就是说,我们可以通过xml/ofd去生成pdf,但是却不能反过来。

一、代码实现

1、pom依赖

 <dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-full</artifactId><version>2.3.3</version> <!-- 请检查最新版本 --></dependency>

2、代码

/*** ofd 转换为 pdf     无签名信息*/@PostMapping("/ofdConverPdf")public Result ofdConverPdf(@RequestBody MailDto mail) throws Exception {String srcOfdPath = "D:\\ofdConverPdf\\a.ofd";String destPdfPath = "D:\\ofdConverPdf\\b.pdf"; // 最终PDF文件try {Path ofdFile = Paths.get(srcOfdPath);Path pdfFile = Paths.get(destPdfPath);ConvertHelper.toPdf(ofdFile, pdfFile); // 一步到位System.out.println("OFD转换PDF成功!文件: " + destPdfPath);// 请替换为你自己的成功返回方法} catch (GeneralConvertException e) {System.err.println("OFD转换PDF失败: " + e.getMessage());e.printStackTrace();return null;}return null;}

3、效果

转换前:
在这里插入图片描述
转换后:
在这里插入图片描述
pdf文件:
在这里插入图片描述

二、相关问题

问题的现象是,在我本地调用该方法,一切正常。部署到服务器后,转换出来的文件,要么是字体加粗了,要么是中文识别失败了,原因是我本地有字体文件,而linux服务器中没有相关的字体文件。

1.转换后的字体加粗

1.1、问题现象图

如下图所示,一些字体被加粗了
在这里插入图片描述

2.中文识别失败

2.1、问题现象图

一些字体识别失败了
在这里插入图片描述

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

三、字体问题解决方案

定位到是字体因素导致的这些现象,我们就可以解决。方案1自然是服务器中加上相关字体,不过有时候会由于各种各样的因素,导致我们修改线上环境字体没那么方便,因此我们就着重介绍代码修复方式。

3.1、服务器加上相关字体

联系运维上传相关字体文件即可。或者可以找到对应的字体文件,自行上传,并刷新字体缓存即可

3.2、代码读取字体

这里需要注意,有时候由于字体的映射关系不一致,导致你即使再resource下加上了相关字体,并且引用了,他也读取不到,这就需要调试了。我下面贴的,亲测可用:

3.2.1、ofd转换pdf代码

public String getPdfFileWithOfdFile(String ofdUrl) {String result = "";try {// 将ofd文件转换为pdf文件if (!ofdUrl.endsWith(".ofd")){return result;}Path tempOfdFile = null;Path tempPdfFile = null;String fileName = "电子行程单"+UUID.randomUUID();try {log.info("准备开始加载本地字体 flag{} {} ",fontsLoaded,ofdUrl);// 加载本地字体文件loadLocalFonts();log.info("开始加载本地字体结束 flag{} {}",fontsLoaded,ofdUrl);tempOfdFile = Files.createTempFile("temp_ofd", ".ofd");try (InputStream in = new URL(ofdUrl).openStream()) {Files.copy(in, tempOfdFile, StandardCopyOption.REPLACE_EXISTING);}tempPdfFile = Files.createTempFile("temp_pdf", ".pdf");// 执行OFD到PDF转换ConvertHelper.toPdf(tempOfdFile, tempPdfFile);byte[] pdfBytes = Files.readAllBytes(tempPdfFile);result = PdfUtil.upload(pdfBytes, fileName, "pdf");}catch (Exception e){log.error("getPdfFileWithOfdFile###error",e);}finally {// 清理临时文件try {if (tempOfdFile != null){Files.deleteIfExists(tempOfdFile);log.info("getPdfFileWithOfdFile 删除临时文件成功tempOfdFile");}if (tempPdfFile != null){log.info("getPdfFileWithOfdFile 删除临时文件成功tempPdfFile");Files.deleteIfExists(tempPdfFile);}} catch (Exception e) {log.error("getPdfFileWithOfdFile 清理临时文件失败: " , e);}}}catch (Exception e){log.error("getPdfFileWithOfdFile error",e);}return result;}

3.2.2、加载字体代码

import org.ofdrw.converter.ConvertHelper;
import org.ofdrw.converter.FontLoader;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;
import javax.mail.search.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;private static volatile boolean fontsLoaded = false;private void loadLocalFonts() {FontLoader fontLoader = FontLoader.getInstance();try {log.info("loadLocalFontsStart{}",fontsLoaded);if (fontsLoaded){return;}synchronized (FONT_LOAD_LOCK){if (fontsLoaded){return;}// 1) 固定从 classpath 的 resources/fonts 加载核心字体Map<String, String> ttf2Family = coreTtf2Family();java.util.Set<String> loaded = loadFontsFromClasspathDir(fontLoader, ttf2Family);if (loaded.isEmpty()) {log.error("未加载到任何字体,请确认 resources/fonts 是否打包");return;}// 2) 角色分配(按是否已加载) -> 使用字体家族名String primary = loaded.contains("simsun.ttf") ? ttf2Family.get("simsun.ttf") : null;         // 宋体String title = loaded.contains("simhei.ttf") ? ttf2Family.get("simhei.ttf") : null;           // 黑体String modern = loaded.contains("msyh.ttf") ? ttf2Family.get("msyh.ttf") : null;              // 微软雅黑String decor = loaded.contains("stkaiti.ttf") ? ttf2Family.get("stkaiti.ttf") : null;         // 楷体String monospace = loaded.contains("simfang.ttf") ? ttf2Family.get("simfang.ttf") : null;     // 仿宋String defaultFontName = primary != null ? primary : (title != null ? title : (modern != null ? modern : ttf2Family.get(loaded.iterator().next())));log.info("字体角色 - 宋体: {}, 黑体: {}, 微软雅黑: {}, 楷体: {}, 仿宋: {}", primary, title, modern, decor, monospace);log.info("默认字体: {}", defaultFontName);// 3) 应用映射(目标均为家族名,避免 ofdrw 无法内嵌字体)applyFontMappings(fontLoader, defaultFontName, primary, title, modern, decor, monospace);fontsLoaded = true;log.info("字体加载完成,默认字体策略: {} ,fontsLoaded {}", defaultFontName,fontsLoaded);}} catch (Exception e) {log.error("loadLocalFonts 加载字体失败", e);}}// 固定核心字体 TTF → 家族名private Map<String, String> coreTtf2Family() {Map<String, String> ttf2Family = new HashMap<>();ttf2Family.put("simsun.ttf", "SimSun");ttf2Family.put("simhei.ttf", "SimHei");ttf2Family.put("msyh.ttf", "Microsoft YaHei");ttf2Family.put("stkaiti.ttf", "STKaiti");ttf2Family.put("simfang.ttf", "FangSong");return ttf2Family;}// 从 classpath: fonts 目录加载字体为临时文件,再交由 FontLoader 加载private java.util.Set<String> loadFontsFromClasspathDir(FontLoader fontLoader, Map<String, String> ttf2Family) {java.util.Set<String> loaded = new java.util.LinkedHashSet<>();for (String ttf : ttf2Family.keySet()) {try (InputStream fontStream = getClass().getClassLoader().getResourceAsStream("fonts/" + ttf)) {if (fontStream == null) {log.warn("未找到字体文件: fonts/{}", ttf);continue;}Path tempFontFile = Files.createTempFile("ofdrw_font_", "_" + ttf);Files.copy(fontStream, tempFontFile, StandardCopyOption.REPLACE_EXISTING);try { tempFontFile.toFile().deleteOnExit(); } catch (Exception ignore) {}fontLoader.loadFont(tempFontFile);loaded.add(ttf);log.info("已加载字体: {} -> {}", ttf, ttf2Family.get(ttf));} catch (Exception ex) {log.warn("加载字体失败 {}: {}", ttf, ex.getMessage());}}return loaded;}// 按家族名设置映射,包含常见中文/西文字体与兜底private void applyFontMappings(FontLoader fontLoader,String defaultFontName,String primary, String title, String modern,String decor, String monospace) {// 先配置具体族,再加兜底,避免被 .* 覆盖if (primary != null) {fontLoader.addSimilarFontReplaceRegexMapping("^宋体$", primary);fontLoader.addSimilarFontReplaceRegexMapping(".*宋体.*", primary);fontLoader.addSimilarFontReplaceRegexMapping(".*SimSun.*", primary);fontLoader.addSimilarFontReplaceRegexMapping(".*NSimSun.*", primary);fontLoader.addSimilarFontReplaceRegexMapping(".*simsun.*", primary);}if (title != null) {fontLoader.addSimilarFontReplaceRegexMapping("^黑体$", title);fontLoader.addSimilarFontReplaceRegexMapping(".*黑体.*", title);fontLoader.addSimilarFontReplaceRegexMapping(".*SimHei.*", title);fontLoader.addSimilarFontReplaceRegexMapping(".*simhei.*", title);fontLoader.addSimilarFontReplaceRegexMapping(".*heiti.*", title);}if (modern != null) {fontLoader.addSimilarFontReplaceRegexMapping("^微软雅黑$", modern);fontLoader.addSimilarFontReplaceRegexMapping(".*微软雅黑.*", modern);fontLoader.addSimilarFontReplaceRegexMapping(".*Microsoft YaHei.*", modern);fontLoader.addSimilarFontReplaceRegexMapping(".*msyh.*", modern);fontLoader.addSimilarFontReplaceRegexMapping(".*yahei.*", modern);}// 楷体(若不存在则回退默认)String kaiti = decor != null ? decor : defaultFontName;fontLoader.addSimilarFontReplaceRegexMapping("^楷体$", kaiti);fontLoader.addSimilarFontReplaceRegexMapping("^KaiTi$", kaiti);fontLoader.addSimilarFontReplaceRegexMapping("^STKaiti$", kaiti);fontLoader.addSimilarFontReplaceRegexMapping("^楷体_GB2312$", kaiti);fontLoader.addSimilarFontReplaceRegexMapping(".*楷体.*", kaiti);fontLoader.addSimilarFontReplaceRegexMapping(".*楷书.*", kaiti);fontLoader.addSimilarFontReplaceRegexMapping(".*华文楷体.*", kaiti);fontLoader.addSimilarFontReplaceRegexMapping(".*方正楷体.*", kaiti);if (monospace != null) {fontLoader.addSimilarFontReplaceRegexMapping("^仿宋$", monospace);fontLoader.addSimilarFontReplaceRegexMapping(".*仿宋.*", monospace);fontLoader.addSimilarFontReplaceRegexMapping(".*FangSong.*", monospace);fontLoader.addSimilarFontReplaceRegexMapping(".*FangSong_GB2312.*", monospace);} else {fontLoader.addSimilarFontReplaceRegexMapping(".*仿宋.*", defaultFontName);fontLoader.addSimilarFontReplaceRegexMapping(".*FangSong.*", defaultFontName);}// 西文字体常见族String westModern = modern != null ? modern : defaultFontName;fontLoader.addSimilarFontReplaceRegexMapping(".*Arial.*", westModern);fontLoader.addSimilarFontReplaceRegexMapping(".*Helvetica.*", westModern);fontLoader.addSimilarFontReplaceRegexMapping(".*Calibri.*", westModern);String westFormal = primary != null ? primary : defaultFontName;fontLoader.addSimilarFontReplaceRegexMapping(".*Times.*", westFormal);fontLoader.addSimilarFontReplaceRegexMapping(".*times.*", westFormal);String westMono = monospace != null ? monospace : defaultFontName;fontLoader.addSimilarFontReplaceRegexMapping(".*Courier.*", westMono);fontLoader.addSimilarFontReplaceRegexMapping(".*courier.*", westMono);// 最后兜底:任何未匹配的字体使用默认字体fontLoader.addSimilarFontReplaceRegexMapping(".*", defaultFontName);}

总结

有一说一,这个字体真是废了我半天劲,明明识别并读取到字体文件了,转换完的pdf还是乱码,最后还是手动加的映射关系才转换成功了。在这里分享一下,大家遇到类似的问题,直接cv大法就行,省的耽误时间捣鼓这些细节了。我们工作中总是会遇到各种各样的问题,就比如只有ofd没有pdf(但是结算和运营需要同时有pdf和ofd才能给客户邮寄),遇到坑,我们分享出来,下一个人就不会同一个问题耽误很长时间了。

http://www.dtcms.com/a/475130.html

相关文章:

  • php网站开发实战教程国外免费搭建网站
  • 崇明区建设镇网站越秀移动网站建设
  • 【Android】MVP架构模式
  • WAV文件结构和PCM数据转存WAV文件
  • 美妆网站开发规划书有没有帮忙做问卷调查的网站
  • 绵阳手机网站建设分公司注册流程及需要的材料
  • 手工活接单在家做有正规网站吗湖南省郴州市天气预报
  • Linux中重定向举例!!
  • 建设厅八大员在哪个网站查询视频拍摄器材
  • 济南网站建设推广服务网站建设的目的及目标
  • php 网站开发缓存有那几种关键词挖掘ppt
  • 三、从 MinIO 存储到 OCR 提取,再到向量索引生成
  • 适合设计师看的设计网站网站建设jnlongji
  • 测试题-2
  • 萝岗哪家网站建设好工作态度
  • 企业网站首页设计公司网站规划与建设心得
  • 东莞连衣裙 东莞网站建设国内常见响应式网站
  • 四川省住房与城乡建设厅网站管网crm软件系统的构成包括
  • 江阴建设局官方网站六安哪里有做推广网站
  • 秦皇岛网站开发哪家好室内设计导航
  • 网站怎么上传代码吗六安市城市建设档案馆网站
  • 什么都不会怎么做网站郑州商城网站建设
  • 五种热门编程语言(Java/C/Python/PHP/C#/C++)在当代软件开发中的综合应用与趋势分析
  • 微淘客网站建设wordpress输入密码查看内容
  • 网站系统修改不了怎么回事杭州网站建设公司代理加盟
  • PowerShell 基础文本处理语法教程
  • 计算机组成原理---计算机系统概述
  • 网站建设设计开发公司云免网站空间
  • wordpress网站特别卡石家庄求职信息网
  • 好的网站制作网站简述企业注册的流程