问题

在使用pdfbox转图片时,出现字体不支持的问题。通用的解决方式是将需要的字体安装到服务器来解决,但是这种方式比较鸡肋,如果我们是集群部署或者需要迁移服务器时,就必须多次安装字体,这是一种很不好的体验。

原因

pdfbox在转换pdf时,会从系统的字体库去初始化字体到内存中,如果要转换的字体已经存在,则直接使用该字体,如果该字体不存在,在源码中对该字体的替补字体进行配置,寻找可替代的字体转换,如果没有可替代的字体则不会输出文字内容。

解决

方案1:安装字体到系统

具体操作,将字体copy到/usr/share/fonts/路径下,使用命令   fc-cache -fv   刷新字体库即可。

方案2:为字体添加已存在的替补字体(需修改源码)。具体操作如下:

在pdfbox 项目中寻找类FontMapperImpl,按照图中红框的代码添加需要的字体规则

方案3:自定义添加字体,这里笔者做了两种处理。
一种是定义一个绝对路径,将字体放在该路径下,初始化时扫描加载这个字体库。具体做法如下: 找到FileSystemFontProvider,在他的构造其中添加绝对路径的字体扫描。可以看到笔者是通过系统变量pdfbox.fontsUrl来获取绝对变量的路径,默认路径为/usr/local/fonts/。这里可以自定义,怎么方便怎么来。eg:-Dpdfbox.fontsUrl=/root/pdf/resources/fonts

 /*** Constructor.*/FileSystemFontProvider(FontCache cache) {this.cache = cache;try {if (LOG.isTraceEnabled()) {LOG.trace("Will search the local system for fonts");}//定义一个用于字体去重的set集合HashSet<String> fileSet = new HashSet<>();// scan the local system for font filesList<File> files = new ArrayList<File>();FontFileFinder fontFileFinder = new FontFileFinder();List<URI> fonts = fontFileFinder.find();for (URI font : fonts) {File file = new File(font);files.add(file);fileSet.add(file.getName());}int localFonts=files.size();if (LOG.isDebugEnabled()) {LOG.debug("Found " + localFonts + " fonts on the local system");}// scan pdfbox.fontsUrl for font filestry {String customerFontsUrl = System.getProperty("pdfbox.fontsUrl");if (customerFontsUrl == null || customerFontsUrl.length() <= 0) {customerFontsUrl = "/usr/local/fonts/";}File fontPath = new File(customerFontsUrl);LOG.warn("scan " + fontPath.getAbsolutePath() + " for font files");if (fontPath.exists()) {File[] tempFile = fontPath.listFiles();Arrays.asList(tempFile).forEach(item -> {if (!fileSet.contains(item.getName())) {fileSet.add(item.getName());files.add(item);}});LOG.warn("Found " + tempFile.length + " fonts on " + fontPath.getAbsolutePath());}else{LOG.warn("Found 0 fonts on " + fontPath.getAbsolutePath());}} catch (Exception e) {LOG.warn("can't scan pdfbox.fontsUrl for font files", e);}//scan customer fonts for font filesList<File> customerFonts = MyFontsScanner.scanCustomerFonts();customerFonts.forEach(item -> {if (!fileSet.contains(item.getName())) {fileSet.add(item.getName());files.add(item);if (LOG.isDebugEnabled()) {LOG.debug("san font " + item.getAbsolutePath());}}});LOG.warn("Found " + customerFonts.size() + " fonts on resources/fonts");LOG.warn("Custom fonts total size:" + (files.size() - localFonts));// load cached FontInfo objects 将磁盘上缓存文件里的字体加载进来List<FSFontInfo> cachedInfos = loadDiskCache(files);if (cachedInfos != null && !cachedInfos.isEmpty()) {fontInfoList.addAll(cachedInfos);} else {//cachedInfos为空 ,代表有新的字体加入,重新构建缓存文件LOG.warn("Building on-disk font cache, this may take a while");LOG.warn("fonts total size:" + files.size());scanFonts(files);//重新构建缓存文件saveDiskCache();LOG.warn("Finished building on-disk font cache, found " +fontInfoList.size() + " fonts");if (LOG.isDebugEnabled()) {fontInfoList.forEach(x -> {try {LOG.debug(" include font: " + x.getFont().getName());} catch (IOException e) {LOG.warn(e);}});}}} catch (AccessControlException e) {LOG.error("Error accessing the file system", e);}}

另一种是将字体放在项目中的resources下,初始化时加载这些字体。具体做法如下,笔者先自定义了一个字体扫描类MyFontsScanner。这个扫描类的作用在于将resources下的字体库写入到一个临时目录中,然后再通过FileSystemFontProvider的加载方式进行加载。这里笔者固定了字体库的目录为resources/fonts/。注意:打包时不要编译字体文件,否则会导致不可用(可使用maven-resources-plugin插件的nonFilteredFileExtensions来排除)。

package org.apache.pdfbox.util;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;/*** @author tangjianghua* date 2020/6/5* time 19:57*/
public class MyFontsScanner {private static final Log LOG = LogFactory.getLog(MyFontsScanner.class);/*** java临时路径在系统变量中的key*/public static final String JAVA_IO_TMPDIR = "java.io.tmpdir";/*** 默认自定义字体库路径在resources/fonts/下*/public static final String BOOT_FONTS_PATH_REGEX = "^BOOT-INF\\/classes\\/fonts\\/[a-zA-Z_\\-0-9]+\\.(?i)(tt[cf])$";/*** scanCustomerFonts and return the list of fonts's absolute path.** @author tangjianghua* date 2020/6/5*/public static List<File> scanCustomerFonts() {List<File> fonts = new ArrayList<File>();try {String path = MyFontsScanner.class.getProtectionDomain().getCodeSource().getLocation().getPath();if (path.startsWith("file:")) {path = path.substring(5, path.length());}LOG.debug(path);if (path.contains("BOOT-INF")) {//springboot 模式path = path.split("!")[0];LOG.debug("项目所属路径:" + path);if (path.endsWith("jar")) {@SuppressWarnings("resource")//获得jar包路径JarFile jFile = new JarFile(path);Enumeration<JarEntry> jarEntrys = jFile.entries();while (jarEntrys.hasMoreElements()) {String name = jarEntrys.nextElement().getName();LOG.trace("read jarEntry :" + name);if (name.matches(BOOT_FONTS_PATH_REGEX)) {File s = writeTempFonts(name);if (LOG.isDebugEnabled()) {LOG.trace("write " + name + " to " + s.getAbsolutePath());}fonts.add(s);}}}} else if (path.contains("WEB-INF")) {//tomcat模式path = path.substring(0, path.indexOf("WEB-INF")) + "WEB-INF/classes/fonts";LOG.debug("fonts path : " + path);File file = new File(path);if (file.exists() && file.isDirectory()) {fonts.addAll(Arrays.asList(file.listFiles()));}} else {LOG.warn("请指定jar包所处路径,或者指定通过pdfbox.fontsUrl自定义字体库的路径");}} catch (IOException e) {LOG.error(e);}return fonts;}/*** scanCustomerFonts and return the list of fonts's absolute path.** @author tangjianghua* date 2020/6/5*/public static Set<String> scanCustomerFontsName() {Set<String> fonts = new HashSet<>();try {String path = MyFontsScanner.class.getProtectionDomain().getCodeSource().getLocation().getPath();if (path.startsWith("file:")) {path = path.substring(5, path.length());}LOG.debug(path);if (path.contains("BOOT-INF")) {//springboot 模式path = path.split("!")[0];LOG.debug("项目所属路径:" + path);if (path.endsWith("jar")) {@SuppressWarnings("resource")//获得jar包路径JarFile jFile = new JarFile(path);Enumeration<JarEntry> jarEntrys = jFile.entries();while (jarEntrys.hasMoreElements()) {String name = jarEntrys.nextElement().getName();LOG.trace("read jarEntry :" + name);if (name.matches(BOOT_FONTS_PATH_REGEX)) {fonts.add(name);}}}} else if (path.contains("WEB-INF")) {//tomcat模式path = path.substring(0, path.indexOf("WEB-INF")) + "WEB-INF/classes/fonts";LOG.debug("fonts path : " + path);File file = new File(path);if (file.exists() && file.isDirectory()) {Arrays.asList(file.listFiles()).forEach(item -> {fonts.add(item.getAbsolutePath().split("classes")[1]);});}} else {LOG.warn("请指定jar包所处路径,或者指定通过pdfbox.fontsUrl自定义字体库的路径");}} catch (IOException e) {LOG.error(e);}return fonts;}/*** 将字体写入临时文件夹,并返回绝对路径** @param entryName* @return 绝对路径* @author tangjianghua* date 2020/6/5*/private static File writeTempFonts(String entryName) {String[] split = entryName.split("/");String fileName = split[split.length - 1];InputStream resourceAsStream = null;try {String fontsTempPath = System.getProperty(JAVA_IO_TMPDIR);if (fontsTempPath == null || fontsTempPath.length() <= 0) {String os = System.getProperty("os.name");if (os.contains("Linux")) {fontsTempPath = "/tmp";} else if (os.contains("Windows")) {fontsTempPath = System.getProperty("user.dir");}}fontsTempPath = fontsTempPath + File.separator + "pdfbox" + File.separator + "fonts";File fontsTempPathFile = new File(fontsTempPath);if (!fontsTempPathFile.exists()) {fontsTempPathFile.mkdirs();}File file = new File(fontsTempPath + File.separator + fileName);if (file.exists()) {return file;}resourceAsStream = Thread.currentThread().getClass().getResourceAsStream("/" + entryName);FileOutputStream fileOutputStream = new FileOutputStream(file);byte[] bytes = new byte[1024];int i;while ((i = resourceAsStream.read(bytes)) != -1) {fileOutputStream.write(bytes, 0, i);}fileOutputStream.flush();fileOutputStream.close();resourceAsStream.close();return file;} catch (Exception e) {LOG.warn("fail to write font into tmp path!", e);return null;} finally {if (resourceAsStream != null) {try {resourceAsStream.close();} catch (Exception e) {e.printStackTrace();}}}}
}

使用时可以根据自己的情况进行选择使用。

git地址:https://github.com/tang-jianghua/pdfbox 可直接打包

pdfbox pdf转图片中的字体问题相关推荐

  1. 直接在PDF文件中改变字体的小技巧

    2019独角兽企业重金招聘Python工程师标准>>> PDF文件大家接触的还是蛮多的,今天要给大家介绍一下直接在PDF文件中改变字体的小技巧,想来应该有蛮多小伙伴需要的. 具体操作 ...

  2. kindle看pdf的文档字体调小了

    kindle看pdf的文档字体调小了,怎么改变 以下是某个帖子上找到的,不知道是不是真的,有机会再去实验下

  3. PDF如何修改文件字体大小

    在使用PDF文件的时候会遇到文件中的文字字体大小不一样还有字体样式不一样的情况,这样会显的突兀不好看,这个时候就需要对文件的字体进行修改编辑,那么具体要怎么操作呢?估计有很多小伙伴都很好奇PDF文件的 ...

  4. PDF如何设置注释字体大小

    PDF文件中设置注释大家都知道该怎么做吗?注释的字体大小又要怎么设置了,今天就来跟各位分享一下!一起来看看吧! 一.迅捷PDF编辑器 1.我们需要在百度中搜索并下载并安装一款PDF编辑器http:// ...

  5. 金闪PDF编辑器:如何更改PDF文件中的字体?

    我们在编辑一篇文章的时候,会根据文章的标题或重点来设置不同的字体,不仅可以使文章内容整体更加整洁美观,还可以突出文章的重点,让我们阅读起来更加方便.大家都知道在编辑Word的时候如何更改字体,但是如果 ...

  6. jspdf添加宋体_jspdf下载pdf文件模糊,字体加粗等样式失效

    jspdf下载pdf文件模糊,字体加粗等样式失效 exportPDF = () => {let { shop } = this.props; let element = document.get ...

  7. Linux字体库问题(PDF转图片中文字缺失)

    问题描述 PDF转图片,图片的中文字体缺失,原PDF如下: 转成图片后结果如下: 中文全部丢失了,而且英文格式也对不上 问题分析 根据B站介绍,是linux服务器缺少字体库导致 问题解决步骤 打包wi ...

  8. java api font_java – PDFBox API:如何更改字体以处理Acro...

    下面的代码在acroform默认资源字典中添加了适当的字体,并替换了默认外观中的名称.调用setValue()时,PDFBox使用新字体重新创建字段的外观流. public static void m ...

  9. matplotlib 的rcParams文件、常见的中文字体问题以及图片中全局字体大小控制

    1 matplotlib的两个常用配置 1.1 解决图片无法生成汉语文字的问题 使用这个命令,可以使得图片中的汉语得到显示,默认是无法显示汉语的. from pylab import mpl mpl. ...

  10. adobe acrobat看PDF文档显示字体发虚,有毛刺的解决办法

    一直使用acrobat阅读pdf文档,有次不小心点到了什么,然后出来一个弹窗,没注意就把它关闭了,结果发现打开不少文档时,字体颜色较浅,笔画也很细,整体感觉有些模糊不清,看着很是吃力.先前由于办理签证 ...

最新文章

  1. List集合的去除重复性练习
  2. POJ2155二维线段树
  3. Angular之filter学习
  4. 记一次 .NET 某三甲医院HIS系统 内存暴涨分析
  5. 使用python获取焦点窗口的进程名称
  6. 2017年云计算行业新动向盘点
  7. 【效率特工队】一款神器,批量手机号码归属地查询最新软件,可导出excel表格,支持全面号段
  8. 大学语言c 离线作业,大学语文C离线作业(2013下).doc
  9. POI操作Microsoft Office 之 操作PPT简单示例(附源码)
  10. PyCharm下载安装以及使用教程
  11. 求解马走棋问题(回溯法)
  12. Flutter(五)沉浸式状态栏
  13. Bazel build Remote-caching
  14. 宇宙有新惊喜了吗?十亿光年外存在巨大的反物质喷泉?
  15. 红米note4出厂系统版本_红米Note4发布!出厂就有MIUI8黑科技
  16. 显示器预算有限,是买1080P 144HZ还是买2K 60HZ?
  17. 数码管点亮中几个常见三极管基极导通状态
  18. 计算机毕业设计宠物网站,《宠物信息网站的设计计算机毕业设计(论文)》.doc
  19. android系统的简单定制
  20. 迅闪2011客户端(0862)安装异常处理及注意事项

热门文章

  1. multus-cni之源码解析
  2. 作为一个大学才开始入门学计算机编程的孩子想要的东西-----听我扯,你蛋疼,他菊紧,我开心
  3. 阿里云ACA试题-云安全典型题19道
  4. 网站三级域名是什么样?
  5. 最新的北京2012年地铁规划图
  6. 13号线ab线规划图_有图有真相,北京13号地铁将拆分为AB两条线
  7. 金士顿U盘被写保护的解决方法(量产)
  8. 10年计算机速度慢加固态硬盘行不行,给超过十年的老笔记本电脑换固态值不值?试过发现还不错...
  9. 操作系统的概念(定义)
  10. 阿里云Oss云存储的使用步骤