From 508cc7589bea56bd41f114307e91083f57034a52 Mon Sep 17 00:00:00 2001 From: Diego Garcia Date: Mon, 22 Jun 2026 22:04:17 -0300 Subject: [PATCH] fix: prevent duplicate font family entries in FontFactoryImp Guard the registerFamily insertion path with a contains check so that calling registerFamily with the same familyName/fullName pair more than once does not grow the family list unboundedly and slow down font lookup. Add a package-private getRegisteredFamily accessor and a unit test to cover the deduplication invariant. Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/lowagie/text/FontFactoryImp.java | 24 ++++++++++++++- .../com/lowagie/text/FontFactoryImpTest.java | 29 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 openpdf/src/test/java/com/lowagie/text/FontFactoryImpTest.java diff --git a/openpdf/src/main/java/com/lowagie/text/FontFactoryImp.java b/openpdf/src/main/java/com/lowagie/text/FontFactoryImp.java index bc2566aa4..e90eb4bce 100644 --- a/openpdf/src/main/java/com/lowagie/text/FontFactoryImp.java +++ b/openpdf/src/main/java/com/lowagie/text/FontFactoryImp.java @@ -55,6 +55,7 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; @@ -515,7 +516,7 @@ public void registerFamily(String familyName, String fullName, String path) { tmp = new ArrayList<>(); tmp.add(fullName); fontFamilies.put(familyName, tmp); - } else { + } else if (!tmp.contains(fullName)) { int fullNameLength = fullName.length(); boolean inserted = false; for (int j = 0; j < tmp.size(); ++j) { @@ -727,6 +728,27 @@ public Set getRegisteredFamilies() { return Utilities.getKeySet(fontFamilies); } + /** + * Gets the font names registered for a given font family. Package-private: intended for + * testing the deduplication done by {@link #registerFamily}, not part of the public API. + * + * @param family the font family (case-insensitive) + * @return an unmodifiable copy of the registered font names for the family, or an empty list if + * the family is not registered + */ + List getRegisteredFamily(String family) { + lock.readLock().lock(); + try { + List members = fontFamilies.get(family.toLowerCase(Locale.ROOT)); + if (members == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(new ArrayList<>(members)); + } finally { + lock.readLock().unlock(); + } + } + /** * Checks if a certain font is registered. * diff --git a/openpdf/src/test/java/com/lowagie/text/FontFactoryImpTest.java b/openpdf/src/test/java/com/lowagie/text/FontFactoryImpTest.java new file mode 100644 index 000000000..50f0a97bf --- /dev/null +++ b/openpdf/src/test/java/com/lowagie/text/FontFactoryImpTest.java @@ -0,0 +1,29 @@ +package com.lowagie.text; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class FontFactoryImpTest { + + /** + * Registering the same family/name combination several times must not add duplicate + * entries to the font family list, otherwise the list grows unbounded and slows down + * the lookup loop in {@link FontFactoryImp#getFont}. + */ + @Test + void registerFamilyShouldNotStoreDuplicateFullNames() { + FontFactoryImp fontFactory = new FontFactoryImp(); + + fontFactory.registerFamily("my-family", "My-Font-Regular", null); + fontFactory.registerFamily("my-family", "My-Font-Regular", null); + fontFactory.registerFamily("my-family", "My-Font-Regular", null); + + // A distinct member of the same family must still be added. + fontFactory.registerFamily("my-family", "My-Font-Bold", null); + + assertThat(fontFactory.getRegisteredFamilies()).contains("my-family"); + assertThat(fontFactory.getRegisteredFamily("my-family")) + .containsExactlyInAnyOrder("My-Font-Regular", "My-Font-Bold"); + } +}