2

We are converting mail messages based on iText 7.1.2 and htmlPDF 2.0.2. The conversion is done within a static method which is called by parallel threads for every html-based message. The code looks simplified like this (streams are closed in a 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);

The for-loop is used to add chinese fonts from the Noto package located in the classpath. In our environment we now see sometimes the following error scenario:

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)

The questions are:

  • 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)?
  • If the DefaultFontProvider is initialized only once -> is this instance thread save?

Thanks in advance!

ithofm
  • 164
  • 10

1 Answers1

2

Short answer is: please, read the documentation.

Answer to question 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)?

You should create a new instance for every conversion as you do now. Font providers are tied to documents, which is stated in the documentation for base FontProvider class. Although if you reuse a font provider it will still most likely work, this is not the way I would recommend.

The documentation of setFontProvider method which you are using is explicit about this:

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.

Answer to question 2

If the DefaultFontProvider is initialized only once -> is this instance thread save?

There is no guarantee on thread safety. Documentation guides you to only use a DefaultFontProvider for a single conversion.

Optimization tips

(in order I would try and apply them)

  1. Take a look at the DefaultFontProvider(boolean, boolean, boolean) constructor. By default pdfHTML loads standard PDF fonts as well as fonts that are shipped with pdfHTML. If you are sure that the fonts you add manually cover all the scripts you use in your HTML files, you can pass false to all the three constructor parameters (new DefaultFontProvider(false, false, false)). But if you are not sure don't do it because it may result in some text missing in the result. Or add the necessary fonts to your collection to be sure.
  2. Reuse FontProgram instances. They are thread-safe and can be used for conversion of many documents. However, this is the default behavior of iText and this will most likely not improve things in your case apart from making the fact explicit.
  3. If you can do point 1., then you can also reuse FontSet if you use another implementation of FontProvider - first, create your DefaultFontProvider once and add all the fonts there once, then, get the FontSet instance via defaultFP.getFontSet(), after which you can create font providers by font set with new FontProvider(fontSet) - this you will have to do each time you do a conversion (see answer to question 1).
Community
  • 1
  • 1
Alexey Subach
  • 11,903
  • 7
  • 34
  • 60
  • Hello and thanks. If I understand this correctly, I can integrate optimization tip 3 in case the used fonts loaded by DefaultFontProvider does not change for all of the convertible documents. So I still can initialize one DefaultFontProvider and use that as a source for the FontSet/FontProvider during the single conversion calls. – Tobias Held Nov 08 '18 at 09:10