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

编程入门 行业动态 更新时间:2024-10-27 10:30:58

ITEXT7 字体处理

  • 前言
  • 一、如何添加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后,如何加载字体信息

这里我们可以看到,它从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是如何获取对应字体和如何设置代替字体的。有问题欢迎交流~

更多推荐

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

本文发布于:2023-06-14 02:55:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1429716.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:源代码   问题解答   字体

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!