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。这时候可能是有多种情况
- 需要确认字体有没有在字体库里?
没有的需要在网上检索对应字体,比如 宋体加粗下载、黑体加粗下载等等 - 需要确认字体是否为 ttf 格式?
对于网上流传的字体,实际上格式是多种多样的,目前itext7支持 ttf 格式的字体,若是只有ttc格式的需要在网上检索字体转换进行处理。 - 需要确认字体名字是否为“英文”?
对于中文和英文各类字体,实际上在字体使用的时候,需要根据字体信息再专门进行配置。对于该信息的查询可以查看FontProgramDescriptor.fontName,该属性即为该字体真正的名称。 该类在FontInfo字体里面,可以在addFont后,通过debug或序列化打印出来 - 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 字体问题解答与相应源代码分析
发布评论