You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2024/05/09 10:54:36 UTC

svn commit: r1917610 - in /pdfbox/trunk/pdfbox/src: main/java/org/apache/pdfbox/cos/ main/java/org/apache/pdfbox/pdfparser/ main/java/org/apache/pdfbox/pdfwriter/ main/java/org/apache/pdfbox/pdmodel/encryption/ main/java/org/apache/pdfbox/pdmodel/inter...

Author: lehmi
Date: Thu May  9 10:54:36 2024
New Revision: 1917610

URL: http://svn.apache.org/viewvc?rev=1917610&view=rev
Log:
PDFBOX-5469: make COSString immutable

Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/COSParser.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSString.java

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java?rev=1917610&r1=1917609&r2=1917610&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java Thu May  9 10:54:36 2024
@@ -47,8 +47,8 @@ public final class COSString extends COS
 {
     private static final Logger LOG = LogManager.getLogger(COSString.class);
 
-    private byte[] bytes;
-    private boolean forceHexForm;
+    private final byte[] bytes;
+    private final boolean forceHexForm;
 
     // legacy behaviour for old PDFParser
     public static final boolean FORCE_PARSING =
@@ -62,7 +62,21 @@ public final class COSString extends COS
      */
     public COSString(byte[] bytes)
     {
-        setValue(bytes);
+        this(bytes, false);
+    }
+
+    /**
+     * Creates a new PDF string from a byte array. This method can be used to read a string from an existing PDF file,
+     * or to create a new byte string.
+     *
+     * @param bytes The raw bytes of the PDF text string or byte string.
+     * @param forceHex forces the hexadecimal presentation of the string if set to true
+     * 
+     */
+    public COSString(byte[] bytes, boolean forceHex)
+    {
+        forceHexForm = forceHex;
+        this.bytes = Arrays.copyOf(bytes, bytes.length);
     }
 
     /**
@@ -72,6 +86,19 @@ public final class COSString extends COS
      */
     public COSString(String text)
     {
+        this(text, false);
+    }
+
+    /**
+     * Creates a new <i>text string</i> from a Java String.
+     *
+     * @param text The string value of the object.
+     * @param forceHex forces the hexadecimal presentation of the string if set to true
+     * 
+     */
+    public COSString(String text, boolean forceHex)
+    {
+        forceHexForm = forceHex;
         // check whether the string uses only characters available in PDFDocEncoding
         boolean isOnlyPDFDocEncoding = true;
         for (char c : text.toCharArray())
@@ -142,30 +169,6 @@ public final class COSString extends COS
     }
 
     /**
-     * Sets the raw value of this string.
-     *
-     * @param value The raw bytes of the PDF text string or byte string.
-     * 
-     * @deprecated to be removed in a future release.
-     */
-    @Deprecated
-    public void setValue(byte[] value)
-    {
-        bytes = value.clone();
-    }
-
-    /**
-     * Sets whether to force the string is to be written in hex form.
-     * This is needed when signing PDF files.
-     *
-     * @param value True to force hex.
-     */
-    public void setForceHexForm(boolean value)
-    {
-        this.forceHexForm = value;
-    }
-
-    /**
      * Returns true if the string is to be written in hex form.
      * 
      * @return true if the COSString is written in hex form
@@ -219,7 +222,7 @@ public final class COSString extends COS
      */
     public byte[] getBytes()
     {
-        return bytes.clone();
+        return Arrays.copyOf(bytes, bytes.length);
     }
 
     /**

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/COSParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/COSParser.java?rev=1917610&r1=1917609&r2=1917610&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/COSParser.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/COSParser.java Thu May  9 10:54:36 2024
@@ -588,7 +588,9 @@ public class COSParser extends BaseParse
         }
         else if (securityHandler != null)
         {
-            securityHandler.decrypt(parsedObject, objKey.getNumber(), objKey.getGeneration());
+            parsedObject = securityHandler.decrypt(parsedObject, objKey.getNumber(),
+                    objKey.getGeneration());
+            parsedObject.setKey(objKey);
         }
 
         if (!endObjectKey.startsWith(ENDOBJ_STRING))

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java?rev=1917610&r1=1917609&r2=1917610&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java Thu May  9 10:54:36 2024
@@ -1429,12 +1429,17 @@ public class COSWriter implements ICOSVi
     {
         if (willEncrypt)
         {
-            pdDocument.getEncryption().getSecurityHandler().encryptString(
+            COSString encryptedCOSString = (COSString) pdDocument.getEncryption()
+                    .getSecurityHandler().encryptString(
                     obj,
                     currentObjectKey.getNumber(),
                     currentObjectKey.getGeneration());
+            COSWriter.writeString(encryptedCOSString, getStandardOutput());
+        }
+        else
+        {
+            COSWriter.writeString(obj, getStandardOutput());
         }
-        COSWriter.writeString(obj, getStandardOutput());
     }
 
     /**

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java?rev=1917610&r1=1917609&r2=1917610&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/encryption/SecurityHandler.java Thu May  9 10:54:36 2024
@@ -444,29 +444,33 @@ public abstract class SecurityHandler<T_
     /**
      * This will dispatch to the correct method.
      *
-     * @param obj    The object to decrypt.
+     * @param obj The object to decrypt.
      * @param objNum The object number.
      * @param genNum The object generation Number.
+     * 
+     * @return the encrypted/decrypted COS object
      *
      * @throws IOException If there is an error getting the stream data.
      */
-    public void decrypt(COSBase obj, long objNum, long genNum) throws IOException
+    public COSBase decrypt(COSBase obj, long objNum, long genNum) throws IOException
     {
         // PDFBOX-4477: only cache strings and streams, this improves speed and memory footprint
         if (obj instanceof COSString)
         {
             if (objects.contains(obj))
             {
-                return;
+                return obj;
             }
-            objects.add(obj);
-            decryptString((COSString) obj, objNum, genNum);
+            // replace the given COSString object with the encrypted/decrypted version
+            COSBase decryptedString = decryptString((COSString) obj, objNum, genNum);
+            objects.add(decryptedString);
+            return decryptedString;
         }
-        else if (obj instanceof COSStream)
+        if (obj instanceof COSStream)
         {
             if (objects.contains(obj))
             {
-                return;
+                return obj;
             }
             objects.add(obj);
             decryptStream((COSStream) obj, objNum, genNum);
@@ -479,6 +483,7 @@ public abstract class SecurityHandler<T_
         {
             decryptArray((COSArray) obj, objNum, genNum);
         }
+        return obj;
     }
 
     /**
@@ -610,7 +615,7 @@ public abstract class SecurityHandler<T_
             // within a dictionary only the following kind of COS objects have to be decrypted
             if (value instanceof COSString || value instanceof COSArray || value instanceof COSDictionary)
             {
-                decrypt(value, objNum, genNum);
+                entry.setValue(decrypt(value, objNum, genNum));
             }
         }
     }
@@ -621,14 +626,15 @@ public abstract class SecurityHandler<T_
      * @param string the string to decrypt.
      * @param objNum The object number.
      * @param genNum The object generation number.
-     *
+     * 
+     * @return the decrypted COSString
      */
-    private void decryptString(COSString string, long objNum, long genNum)
+    private COSBase decryptString(COSString string, long objNum, long genNum)
     {
         // String encrypted with identity filter
         if (COSName.IDENTITY.equals(stringFilterName))
         {
-            return;
+            return string;
         }
         
         ByteArrayInputStream data = new ByteArrayInputStream(string.getBytes());
@@ -636,12 +642,13 @@ public abstract class SecurityHandler<T_
         try
         {
             encryptData(objNum, genNum, data, outputStream, true /* decrypt */);
-            string.setValue(outputStream.toByteArray());
+            return new COSString(outputStream.toByteArray());
         }
         catch (IOException ex)
         {
             LOG.error("Failed to decrypt COSString of length {} in object {}: {}",
                     string.getBytes().length, objNum, ex.getMessage(), ex);
+            return string;
         }
     }
 
@@ -652,14 +659,16 @@ public abstract class SecurityHandler<T_
      * @param objNum The object number.
      * @param genNum The object generation number.
      *
+     * @return the encrypted COSString
+     * 
      * @throws IOException If an error occurs writing the new string.
      */
-    public void encryptString(COSString string, long objNum, int genNum) throws IOException
+    public COSBase encryptString(COSString string, long objNum, int genNum) throws IOException
     {
         ByteArrayInputStream data = new ByteArrayInputStream(string.getBytes());
         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
         encryptData(objNum, genNum, data, buffer, false /* encrypt */);
-        string.setValue(buffer.toByteArray());
+        return new COSString(buffer.toByteArray());
     }
 
     /**
@@ -675,7 +684,7 @@ public abstract class SecurityHandler<T_
     {
         for (int i = 0; i < array.size(); i++)
         {
-            decrypt(array.get(i), objNum, genNum);
+            array.set(i, decrypt(array.get(i), objNum, genNum));
         }
     }
 

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java?rev=1917610&r1=1917609&r2=1917610&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java Thu May  9 10:54:36 2024
@@ -394,8 +394,7 @@ public class PDSignature implements COSO
      */
     public void setContents(byte[] bytes)
     {
-        COSString string = new COSString(bytes);
-        string.setForceHexForm(true);
+        COSString string = new COSString(bytes, true);
         dictionary.setItem(COSName.CONTENTS, string);
     }
 

Modified: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSString.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSString.java?rev=1917610&r1=1917609&r2=1917610&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSString.java (original)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/cos/TestCOSString.java Thu May  9 10:54:36 2024
@@ -59,15 +59,14 @@ class TestCOSString extends TestCOSBase
     {
         String inputString = "Test with a text and a few numbers 1, 2 and 3";
         String pdfHex = "<" + createHex(inputString) + ">";
-        COSString cosStr = new COSString(inputString);
-        cosStr.setForceHexForm(true);
+        COSString cosStr = new COSString(inputString, true);
         writePDFTests(pdfHex, cosStr);
 
         COSString escStr = new COSString(ESC_CHAR_STRING);
         writePDFTests("(" + ESC_CHAR_STRING_PDF_FORMAT + ")", escStr);
-        escStr.setForceHexForm(true);
+        COSString escStrHex = new COSString(ESC_CHAR_STRING, true);
         // Escape characters not escaped in hex version
-        writePDFTests("<" + createHex(ESC_CHAR_STRING) + ">", escStr);
+        writePDFTests("<" + createHex(ESC_CHAR_STRING) + ">", escStrHex);
     }
 
     /**
@@ -265,8 +264,8 @@ class TestCOSString extends TestCOSBase
         testSubj.accept(visitor);
         assertEquals("(" + ESC_CHAR_STRING_PDF_FORMAT + ")", outStream.toString());
         outStream.reset();
-        testSubj.setForceHexForm(true);
-        testSubj.accept(visitor);
+        COSString testSubjHex = new COSString(ESC_CHAR_STRING, true);
+        testSubjHex.accept(visitor);
         assertEquals("<" + createHex(ESC_CHAR_STRING) + ">", outStream.toString());
     }
 
@@ -287,8 +286,7 @@ class TestCOSString extends TestCOSBase
             COSString y1 = new COSString("Test");
             assertEquals(x1, y1);
             assertEquals(y1, x1);
-            COSString x2 = new COSString("Test");
-            x2.setForceHexForm(true);
+            COSString x2 = new COSString("Test", true);
             // also if x != y then y != x
             assertNotEquals(x1, x2);
             assertNotEquals(x2, x1);
@@ -316,8 +314,8 @@ class TestCOSString extends TestCOSBase
         assertNotEquals(str1.hashCode(), str2.hashCode());
         COSString str3 = new COSString("Test1");
         assertEquals(str1.hashCode(), str3.hashCode());
-        str3.setForceHexForm(true);
-        assertNotEquals(str1.hashCode(), str3.hashCode());
+        COSString str3Hex = new COSString("Test1", true);
+        assertNotEquals(str1.hashCode(), str3Hex.hashCode());
     }
 
     /**