iText 7、htmlPDF 2 - DefaultFontProvider 的并行使用

iText 7, htmlPDF 2 - Parallel usage of DefaultFontProvider

我们正在根据 iText 7.1.2htmlPDF 2.0.2 转换邮件信息。转换是在一个静态方法中完成的,该方法由并行线程为每个基于 html 的消息调用。代码看起来像这样简化(流在 finally 块中关闭):

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)

问题是:

提前致谢!

简短的回答是:请阅读文档。

问题 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)?

您应该像现在一样为每次转化创建一个新实例。字体提供程序与文档相关联,这在 base FontProvider class 的文档中有说明。虽然如果您重复使用字体提供程序,它仍然很可能会起作用,但这不是我推荐的方式。

您正在使用的 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 进行一次转换。

优化提示

(为了我会尝试应用它们)

  1. 看看 DefaultFontProvider(boolean, boolean, boolean) 构造函数。默认情况下,pdfHTML 加载标准 PDF 字体以及 pdfHTML 附带的字体。如果您确定您手动添加的字体涵盖了您在 HTML 文件中使用的所有脚本,则可以将 false 传递给所有三个构造函数参数 (new DefaultFontProvider(false, false, false))。但是,如果您不确定,请不要这样做,因为这可能会导致结果中缺少某些文本。或将必要的字体添加到您的 collection 以确保。
  2. 重用 FontProgram 个实例。它们是thread-safe,可用于许多文档的转换。但是,这是 iText 的默认行为,除了使事实明确之外,这很可能不会改善您的情况。
  3. 如果你可以做到 1.,那么如果你使用 FontProvider 的另一个实现,你也可以重用 FontSet - 首先,创建你的 DefaultFontProvider once 并在那里添加所有字体 once,然后,通过 defaultFP.getFontSet() 获取 FontSet 实例,之后您可以创建字体提供程序通过 new FontProvider(fontSet) 设置的字体 - 每次进行转换时都必须这样做(请参阅问题 1 的答案)。