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();


Reply via email to