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;
}
/**