Author: tilman
Date: Tue Mar 24 10:55:17 2026
New Revision: 1932500

Log:
PDFBOX-5660: optimize / refactor, as suggested by Valery Bokov; closes #424

Modified:
   
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java

Modified: 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
==============================================================================
--- 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
 Tue Mar 24 10:55:13 2026        (r1932499)
+++ 
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
 Tue Mar 24 10:55:17 2026        (r1932500)
@@ -442,6 +442,28 @@ public abstract class SecurityHandler<T_
     }
 
     /**
+     * This will decrypt a string if it is not in set of the objects.
+     *
+     * @param string The string to decrypt.
+     * @param objNum The object number.
+     * @param genNum The object generation Number.
+     *
+     * @return the encrypted/decrypted COS object
+     */
+    private COSBase decryptStringIfAbsent(COSString string, long objNum, long 
genNum)
+    {
+        // PDFBOX-4477: only cache strings and streams, this improves speed 
and memory footprint
+        if (objects.contains(string))
+        {
+            return string;
+        }
+        // replace the given COSString object with the encrypted/decrypted 
version
+        COSBase decryptedString = decryptString(string, objNum, genNum);
+        objects.add(decryptedString);
+        return decryptedString;
+    }
+
+    /**
      * This will dispatch to the correct method.
      *
      * @param obj The object to decrypt.
@@ -457,36 +479,44 @@ public abstract class SecurityHandler<T_
         // PDFBOX-4477: only cache strings and streams, this improves speed 
and memory footprint
         if (obj instanceof COSString)
         {
-            if (objects.contains(obj))
-            {
-                return obj;
-            }
-            // replace the given COSString object with the encrypted/decrypted 
version
-            COSBase decryptedString = decryptString((COSString) obj, objNum, 
genNum);
-            objects.add(decryptedString);
-            return decryptedString;
+            return decryptStringIfAbsent((COSString)obj, objNum, genNum);
         }
-        if (obj instanceof COSStream)
+        else if (obj instanceof COSStream)
         {
-            if (objects.contains(obj))
-            {
-                return obj;
-            }
-            objects.add(obj);
-            decryptStream((COSStream) obj, objNum, genNum);
+            return decryptStreamIfAbsent((COSStream)obj, objNum, genNum);
         }
         else if (obj instanceof COSDictionary)
         {
-            decryptDictionary((COSDictionary) obj, objNum, genNum);
+            return decryptDictionary((COSDictionary) obj, objNum, genNum);
         }
         else if (obj instanceof COSArray)
         {
-            decryptArray((COSArray) obj, objNum, genNum);
+            return decryptArray((COSArray) obj, objNum, genNum);
         }
         return obj;
     }
 
     /**
+     * This will decrypt a stream if it is not in set of the objects.
+     *
+     * @param stream The stream to decrypt.
+     * @param objNum The object number.
+     * @param genNum The object generation Number.
+     *
+     * @return the encrypted/decrypted COS object
+     */
+    private COSBase decryptStreamIfAbsent(COSStream stream, long objNum, long 
genNum) throws IOException
+    {
+        if (!objects.contains(stream))
+        {
+            objects.add(stream);
+            decryptStream(stream, objNum, genNum);
+        }
+
+        return stream;
+    }
+
+    /**
      * This will decrypt a stream.
      *
      * @param stream The stream to decrypt.
@@ -589,14 +619,16 @@ public abstract class SecurityHandler<T_
      * @param objNum The object number.
      * @param genNum The object generation number.
      *
+     * @return the encrypted/decrypted COS object
+     *
      * @throws IOException If there is an error creating a new string.
      */
-    private void decryptDictionary(COSDictionary dictionary, long objNum, long 
genNum) throws IOException
+    private COSBase decryptDictionary(COSDictionary dictionary, long objNum, 
long genNum) throws IOException
     {
         if (dictionary.getItem(COSName.CF) != null)
         {
             // PDFBOX-2936: avoid orphan /CF dictionaries found in US govt 
"I-" files
-            return;
+            return dictionary;
         }
         COSName type = dictionary.getCOSName(COSName.TYPE);
         boolean isSignature = COSName.SIG.equals(type) || 
COSName.DOC_TIME_STAMP.equals(type) ||
@@ -613,11 +645,25 @@ public abstract class SecurityHandler<T_
             }
             COSBase value = entry.getValue();
             // within a dictionary only the following kind of COS objects have 
to be decrypted
-            if (value instanceof COSString || value instanceof COSArray || 
value instanceof COSDictionary)
+            if (value instanceof COSString)
+            {
+                entry.setValue(decryptStringIfAbsent((COSString)value, objNum, 
genNum));
+            }
+            else if (value instanceof COSArray)
             {
-                entry.setValue(decrypt(value, objNum, genNum));
+                entry.setValue(decryptArray((COSArray) value, objNum, genNum));
+            }
+            else if (value instanceof COSStream)
+            {
+                entry.setValue(decryptStreamIfAbsent((COSStream)value, objNum, 
genNum));
+            }
+            else if (value instanceof COSDictionary)
+            {
+                entry.setValue(decryptDictionary((COSDictionary)value, objNum, 
genNum));
             }
         }
+
+        return dictionary;
     }
 
     /**
@@ -678,14 +724,18 @@ public abstract class SecurityHandler<T_
      * @param objNum The object number.
      * @param genNum The object generation number.
      *
+     * @return the encrypted/decrypted COS object
+     *
      * @throws IOException If there is an error accessing the data.
      */
-    private void decryptArray(COSArray array, long objNum, long genNum) throws 
IOException
+    private COSBase decryptArray(COSArray array, long objNum, long genNum) 
throws IOException
     {
         for (int i = 0; i < array.size(); i++)
         {
             array.set(i, decrypt(array.get(i), objNum, genNum));
         }
+
+        return array;
     }
 
     /**

Reply via email to