Author: lehmi Date: Thu May 16 17:41:54 2024 New Revision: 1917768 URL: http://svn.apache.org/viewvc?rev=1917768&view=rev Log: PDFBOX-5819: added test to ensure the thread safety of Type2CharStringParser as provided by Patrick Corless, closes #193
Modified: pdfbox/branches/2.0/fontbox/src/test/java/org/apache/fontbox/cff/CFFParserTest.java Modified: pdfbox/branches/2.0/fontbox/src/test/java/org/apache/fontbox/cff/CFFParserTest.java URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/fontbox/src/test/java/org/apache/fontbox/cff/CFFParserTest.java?rev=1917768&r1=1917767&r2=1917768&view=diff ============================================================================== --- pdfbox/branches/2.0/fontbox/src/test/java/org/apache/fontbox/cff/CFFParserTest.java (original) +++ pdfbox/branches/2.0/fontbox/src/test/java/org/apache/fontbox/cff/CFFParserTest.java Thu May 16 17:41:54 2024 @@ -19,8 +19,15 @@ import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.Thread.UncaughtExceptionHandler; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Before; import org.junit.Test; /** @@ -29,6 +36,15 @@ import org.junit.Test; */ public class CFFParserTest { + private static CFFType1Font testCFFType1Font; + + @Before + public void loadCFFFont() throws IOException + { + List<CFFFont> fonts = readFont(new FileInputStream("target/fonts/SourceSansProBold.otf")); + testCFFType1Font = (CFFType1Font) fonts.get(0); + } + /** * PDFBOX-4038: Test whether BlueValues and other delta encoded lists are read correctly. The * test file is from FOP-2432. @@ -38,41 +54,108 @@ public class CFFParserTest @Test public void testDeltaLists() throws IOException { - List<CFFFont> fonts = readFont(new FileInputStream("target/fonts/SourceSansProBold.otf")); - CFFType1Font font = (CFFType1Font) fonts.get(0); @SuppressWarnings("unchecked") - List<Number> blues = (List<Number>)font.getPrivateDict().get("BlueValues"); + List<Number> blues = (List<Number>) testCFFType1Font.getPrivateDict().get("BlueValues"); // Expected values found for this font assertNumberList("Blue values are different than expected: " + blues.toString(), new int[]{-12, 0, 496, 508, 578, 590, 635, 647, 652, 664, 701, 713}, blues); @SuppressWarnings("unchecked") - List<Number> otherBlues = (List<Number>)font.getPrivateDict().get("OtherBlues"); + List<Number> otherBlues = (List<Number>) testCFFType1Font.getPrivateDict() + .get("OtherBlues"); assertNumberList("Other blues are different than expected: " + otherBlues.toString(), new int[]{-196, -184}, otherBlues); @SuppressWarnings("unchecked") - List<Number> familyBlues = (List<Number>)font.getPrivateDict().get("FamilyBlues"); + List<Number> familyBlues = (List<Number>) testCFFType1Font.getPrivateDict() + .get("FamilyBlues"); assertNumberList("Other blues are different than expected: " + familyBlues.toString(), new int[]{-12, 0, 486, 498, 574, 586, 638, 650, 656, 668, 712, 724}, familyBlues); @SuppressWarnings("unchecked") - List<Number> familyOtherBlues = (List<Number>)font.getPrivateDict().get("FamilyOtherBlues"); + List<Number> familyOtherBlues = (List<Number>) testCFFType1Font.getPrivateDict() + .get("FamilyOtherBlues"); assertNumberList("Other blues are different than expected: " + familyOtherBlues.toString(), new int[]{-217, -205}, familyOtherBlues); @SuppressWarnings("unchecked") - List<Number> stemSnapH = (List<Number>)font.getPrivateDict().get("StemSnapH"); + List<Number> stemSnapH = (List<Number>) testCFFType1Font.getPrivateDict().get("StemSnapH"); assertNumberList("StemSnapH values are different than expected: " + stemSnapH.toString(), new int[]{115}, stemSnapH); @SuppressWarnings("unchecked") - List<Number> stemSnapV = (List<Number>)font.getPrivateDict().get("StemSnapV"); + List<Number> stemSnapV = (List<Number>) testCFFType1Font.getPrivateDict().get("StemSnapV"); assertNumberList("StemSnapV values are different than expected: " + stemSnapV.toString(), new int[]{146, 150}, stemSnapV); } + /** + * PDFBOX-5819: ensure thread safety of Type2CharStringParser when parsing the path of a glyph. + * + * @throws InterruptedException + */ + @Test + public void testMultiThreadParse() throws InterruptedException + { + CountDownLatch latch = new CountDownLatch(2); + PathRunner pathRunner1 = new PathRunner(latch); + PathRunner pathRunner2 = new PathRunner(latch); + + PrivateUncaughtExceptionHandler handler = new PrivateUncaughtExceptionHandler(); + + Thread thread1 = new Thread(pathRunner1); + thread1.setUncaughtExceptionHandler(handler); + Thread thread2 = new Thread(pathRunner2); + thread2.setUncaughtExceptionHandler(handler); + + thread1.start(); + thread2.start(); + + latch.await(); + assertFalse(handler.wasCalled.get()); + } + + private class PrivateUncaughtExceptionHandler implements UncaughtExceptionHandler + { + AtomicBoolean wasCalled = new AtomicBoolean(false); + @Override + public void uncaughtException(Thread t, Throwable e) + { + wasCalled.set(true); + } + }; + + private class PathRunner implements Runnable + { + private final CountDownLatch latch; + + public PathRunner(CountDownLatch latch) + { + this.latch = latch; + } + + @Override + public void run() + { + try + { + for (char i = 33; i < 126; i++) + { + testCFFType1Font.getPath(String.valueOf(i)); + } + } + catch (Exception e) + { + throw new IllegalStateException(e); + } + finally + { + latch.countDown(); + } + } + } + private List<CFFFont> readFont(InputStream in) throws IOException { ByteArrayOutputStream content = new ByteArrayOutputStream();