Skip to content

FontUtils.setSansFontsAsDefault is not thread-safe #272

Open
@stanislaw89

Description

@stanislaw89

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions