我们正在基于 iText 7.1.2
和 htmlPDF 2.0.2
转换邮件消息。转换是在静态方法中完成的,该方法由并行线程为每个基于 html 的消息调用。代码看起来像这样简化(流在finally block 中关闭):
ConverterProperties properties = new ConverterProperties();
FontProvider fontProvider = new DefaultFontProvider();
for (String font : ITEXT_FONTS) {
FontProgram fontProgram = FontProgramFactory.createFont(font);
fontProvider.addFont(fontProgram);
}
properties.setFontProvider(fontProvider);
fos = new FileOutputStream(targetFile);
HtmlConverter.convertToPdf(is, fos, properties);
for循环用于从位于类路径的Noto
包中添加中文字体。在我们的环境中,我们现在有时会看到以下错误情况:
Caused by: java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3236)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at com.itextpdf.io.util.StreamUtil.inputStreamToArray(StreamUtil.java:212)
at com.itextpdf.html2pdf.resolver.font.DefaultFontProvider.addShippedFreeFonts(DefaultFontProvider.java:111)
at com.itextpdf.html2pdf.resolver.font.DefaultFontProvider.<init>(DefaultFontProvider.java:97)
at com.itextpdf.html2pdf.resolver.font.DefaultFontProvider.<init>(DefaultFontProvider.java:81)
问题是:
- 每次调用时创建 DefaultFontProvider 是否合法,还是应该只有一个实例(例如,由于创建成本)?
- 如果 DefaultFontProvider 仅初始化一次 -> 该实例是否线程保存?
提前致谢!
最佳答案
简短的回答是:请阅读文档。
问题 1 的答案
Is the creation of the DefaultFontProvider legit for every single call or should there be only one instance (e.g. because of the costs of creation)?
您应该像现在一样为每次转化创建一个新实例。字体提供程序与文档相关联,这在 FontProvider
基类的文档中进行了说明。尽管如果您重用字体提供程序,它仍然很可能有效,但这不是我推荐的方式。
您正在使用的 setFontProvider
方法的文档对此有明确说明:
Please note that FontProvider instances cannot be reused across several documents and thus as soon as you set this property, this ConverterProperties instance becomes only useful for a single HTML conversion.
回答问题2
If the DefaultFontProvider is initialized only once -> is this instance thread save?
无法保证线程安全。文档指导您仅使用 DefaultFontProvider
进行单次转换。
优化技巧
(以便我尝试应用它们)
- 查看
DefaultFontProvider(boolean, boolean, boolean)
构造函数。默认情况下,pdfHTML 加载标准 PDF 字体以及 pdfHTML 附带的字体。如果您确定手动添加的字体涵盖了 HTML 文件中使用的所有脚本,则可以将false
传递给所有三个构造函数参数 (new DefaultFontProvider(false, false, false )
)。但如果您不确定,请不要这样做,因为这可能会导致结果中丢失一些文本。或者将必要的字体添加到您的 Collection 中以确保确定。 - 重用
FontProgram
实例。它们是线程安全的,可用于许多文档的转换。但是,这是 iText 的默认行为,除了明确事实之外,这很可能不会改善您的情况。 - 如果您可以做到
1.
,那么如果您使用FontProvider
的另一个实现,您也可以重用FontSet
- 首先,创建您的DefaultFontProvider
once 并添加所有字体once,然后通过defaultFP 获取
,之后您可以使用FontSet
实例。 getFontSet()new FontProvider(fontSet)
通过字体集创建字体提供程序 - 每次进行转换时都必须执行此操作(请参阅问题 1 的答案) )。
关于java - iText 7、htmlPDF 2 - DefaultFontProvider 的并行使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52813674/