Description
The FontUtils.setSansFontsAsDefault
is implemented as
private static final Map<String, PDFont> defaultFonts = new HashMap<>();
...
public static void setSansFontsAsDefault(PDDocument document) {
defaultFonts.put("font", loadFont(document, "fonts/FreeSans.ttf"));
defaultFonts.put("fontBold", loadFont(document, "fonts/FreeSansBold.ttf"));
defaultFonts.put("fontItalic", loadFont(document, "fonts/FreeSansOblique.ttf"));
defaultFonts.put("fontBoldItalic", loadFont(document, "fonts/FreeSansBoldOblique.ttf"));
}
Then it's used like
public Paragraph(...) {
...
// check if we have different default font for italic and bold text
if (FontUtils.getDefaultfonts().isEmpty()) {
fontBold = PDType1Font.HELVETICA_BOLD;
fontItalic = PDType1Font.HELVETICA_OBLIQUE;
fontBoldItalic = PDType1Font.HELVETICA_BOLD_OBLIQUE;
} else {
fontBold = FontUtils.getDefaultfonts().get("fontBold");
fontBoldItalic = FontUtils.getDefaultfonts().get("fontBoldItalic");
fontItalic = FontUtils.getDefaultfonts().get("fontItalic");
}
...
}
There are two issues related to multi-thread environments.
First, the defaultFonts
map is not thread-safe; it's possible to get a ConcurrentModificationException
if FontUtils.setSansFontsAsDefault
is called from different threads simultaneously.
The second issue is that fonts are shared between documents.
When we call FontUtils.setSansFontsAsDefault(document)
, it may look like we configure the default fonts for the document passed as the parameter. However, this is not true; the document used to load the font (PDType0Font
), which keeps the reference to the document (PDType0Font.embedder->document
). As a result, the mutable non-thread safe set TrueTypeEmbedder.subsetCodePoints
is accessed via multiple threads and, therefore, throws ConcurrentModificationException
.