itext7 字体问题解答与相应源代码分析


前言

本文主要是用于解决itext7 添加字体时遇到的问题分析及解决方案。


一、如何添加itext7 没有的字体?

itext pdf 提供了多种对字体的添加,例如:

FontProvider fp = new FontProvider();
// 该方法为通过添加font的路径的方式让FontProvider对象自行加载字体列表
fp.addDirectory(prePath + "config\\fonts");
// 该方法的优点是便捷,缺点是对于字体的同族支持效果较差容易出现无效果
FontProvider fp = new FontProvider();
// 该方法类似于上面,可以根据设置的路径加载单个字体
fp.addFont(fontPath);
// fp里面的其他方法基本与addFont相同所添加的字体属性基本一致
FontProvider fp = new FontProvider();
// 该方法与第二种不同的在于可以设置alias 
fp.getFontSet().addFont(prePath + "config\\fonts\\Times New Roman.ttf", null, "timesnewromanpsmt");

二、部分字体添加无效

添加字体的方法根据itext7的源码是有很多种的,但是有时候对于一些字体却没有效果,被设置成默认字体Helvetica。这时候可能是有多种情况

  1. 需要确认字体有没有在字体库里?
    没有的需要在网上检索对应字体,比如 宋体加粗下载、黑体加粗下载等等
  2. 需要确认字体是否为 ttf 格式?
    对于网上流传的字体,实际上格式是多种多样的,目前itext7支持 ttf 格式的字体,若是只有ttc格式的需要在网上检索字体转换进行处理。
  3. 需要确认字体名字是否为“英文”?
    对于中文和英文各类字体,实际上在字体使用的时候,需要根据字体信息再专门进行配置。对于该信息的查询可以查看FontProgramDescriptor.fontName,该属性即为该字体真正的名称。 该类在FontInfo字体里面,可以在addFont后,通过debug或序列化打印出来
  4. html2pdf 的时候字体无效
    该问题分为两种情况:一种是第三点描述的内容;另一种是 对于html文件 通常是由office软件另存得到的,再生成的html文件里会蕴含多种对字体样式的描述标签“font-family”、“mso-ascii-font-family”、“mso-fareast-font-family”等。对于html有用的类型为“font-family”,但是其他两种的字体由于某些操作下会与其不一致且office识别的是另外两种,所以造成字体在office里是正常的,但是html就变掉了。这种情况需要编写程序对整个文档进行梳理,自动使得三种字体一致(把“mso-ascii-font-family”的值设置到“font-family”上)。

三、源码分析

对于itext7来说,它是这样判断和选择字体的:

//该行为html文本转换为itext的对象代码
List<IElement> iElements = HtmlConverter.convertToElements(html, props);
//我们从对象iElements里面可以找到Text文本对象,该对象的properties里存放解析出来的改文本配置信息,
//比如 20-> 字体名称  95 ->加粗|正常  94-> 斜体 等信息,当我们发现一段文本字体显示异常,
//可以查看是否有20码,没有说明字体没有被正确识别,需要修改文本里的字体名称

解析html文本后的属性图片
然后我们再来看下在解析完成html后,如何加载字体信息
选择字体
这里我们可以看到,它从Text文本里提取字体名称,然后在同个文件里的2461行,设置是否加粗、加斜

    FontCharacteristics createFontCharacteristics() {
        FontCharacteristics fc = new FontCharacteristics();
        //粗
        if (this.hasProperty(Property.FONT_WEIGHT)) {
            fc.setFontWeight((String) this.<Object>getProperty(Property.FONT_WEIGHT));
        }
        // 斜
        if (this.hasProperty(Property.FONT_STYLE)) {
            fc.setFontStyle((String) this.<Object>getProperty(Property.FONT_STYLE));
        }
        return fc;
    }

这里是为了构建字体选择器,用于下一步获取字体内容 在这里插入图片描述

    public FontSelector(Collection<FontInfo> allFonts, List<String> fontFamilies, FontCharacteristics fc) {
        this.fonts = new ArrayList<>(allFonts);
        //Possible issue in .NET, virtual protected member in constructor.
        Collections.sort(this.fonts, getComparator(fontFamilies, fc));
    }

在实例化FontSelector对象的时候,会对目前加载的所有字体进行排序,该排序是通过计算所有的字体与当前字体的信息吻合度,谁吻合度最高,谁排第一个。便于下一步的字体对象获取。

if (!fontFamilySetByCharacteristics) {
      // if alias is set, fontInfo's descriptor should not be checked
      if (!"".equals(fontFamily)
            && (null == fontInfo.getAlias()
            && null != fontDescriptor.getFamilyNameLowerCase()
            && fontDescriptor.getFamilyNameLowerCase().equals(fontFamily)
            || (null != fontInfo.getAlias() 
                 && fontInfo.getAlias().toLowerCase().equals(fontFamily)))) {
                    score += FONT_FAMILY_EQUALS_AWARD;
       } else {
          if (!isLastFontFamilyToBeProcessed) {
               return score;
          }
        }
    }

在FontSelector类201行可以看到排序时,会判断字体名称,要是字体名称一样就加分。在字体名称不一样的情况下字体alias名一样也可以加分。 所以我们上面在添加字体的时候,设置字体别名就是为了让字体族(粗、斜和粗斜)可以加分,排到前面(第一)。
该判断下面是判断当前字体是否加粗 加斜,吻合也加分从而让最一致的字体项得分最高。
具体的字体选择

可以看到,这边是根据排序后的字体列表进行循环的。由于文字在字体里面基本都是存在的,所以排在第一个的几乎就会被设置为当前字体。这里之所以说几乎,是因为对于外国字体,他是没有汉字的,所以单出现汉字被设置为外国字体或开始的时候没识别到字体时(默认字体不支持中文),这边就会根据排序依次往下判断哪个比较吻合(粗、斜和粗斜)且包含中文的,找到一个替代字体进行显示。在图片中也可以看到itext对于字体是按字提取,需要哪个提取哪个,不是全部都提取,保障了生成的pdf不会太大。


总结

通过上述内容,应该是可以比较清楚的了解了itext7是如何获取对应字体和如何设置代替字体的。有问题欢迎交流~

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值